From 924de8b08f48ca7ef7bed8d5ba0cf4297a4b7848 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mart=C3=ADn=20Lucas=20Golini?= Date: Sun, 21 Jun 2020 17:18:41 -0300 Subject: [PATCH] TextDocument: Added auto indent type and width detection. Fixed a bug in UIListBoxIitem. Minor refactor in LuaPatternMatcher. --- TODO.md | 2 - include/eepp/system/luapatternmatcher.hpp | 15 ++++-- include/eepp/ui/doc/textdocument.hpp | 13 +++-- projects/linux/ee.files | 1 + src/eepp/system/luapatternmatcher.cpp | 21 +++++--- src/eepp/ui/doc/syntaxdefinitionmanager.cpp | 6 +-- src/eepp/ui/doc/syntaxtokenizer.cpp | 4 +- src/eepp/ui/doc/textdocument.cpp | 59 +++++++++++++++++++-- src/eepp/ui/uicodeeditor.cpp | 1 - src/eepp/ui/uilistboxitem.cpp | 3 +- 10 files changed, 98 insertions(+), 27 deletions(-) diff --git a/TODO.md b/TODO.md index 857df0c19..58d1a886e 100644 --- a/TODO.md +++ b/TODO.md @@ -7,8 +7,6 @@ ## TextDocument -* Add indentation type auto-detection. - * Add multi-line search and replace. * Add auto-close brackets. diff --git a/include/eepp/system/luapatternmatcher.hpp b/include/eepp/system/luapatternmatcher.hpp index 605d1472c..1ad78b116 100644 --- a/include/eepp/system/luapatternmatcher.hpp +++ b/include/eepp/system/luapatternmatcher.hpp @@ -13,17 +13,24 @@ class EE_API LuaPatternMatcher { int end; }; + static std::string match( const std::string& string, const std::string& pattern ); + LuaPatternMatcher( const std::string& pattern ); bool matches( const char* stringSearch, int stringStartOffset, - LuaPatternMatcher::Match* matches, size_t stringLength ); + LuaPatternMatcher::Match* matchList, size_t stringLength ); - bool find( const char* stringSearch, int stringStartOffset, int& startMatch, int& endMatch, + bool matches( const std::string& str, LuaPatternMatcher::Match* matchList, + int stringStartOffset = 0 ) { + return matches( str.c_str(), stringStartOffset, matchList, str.size() ); + } + + bool find( const char* stringSearch, int& startMatch, int& endMatch, int stringStartOffset = 0, int stringLength = 0, int returnMatchIndex = 0 ); - bool find( const std::string& s, int offset, int& startMatch, int& endMatch, + bool find( const std::string& s, int& startMatch, int& endMatch, int offset = 0, int returnedMatchIndex = 0 ) { - return find( s.c_str(), offset, startMatch, endMatch, s.size(), returnedMatchIndex ); + return find( s.c_str(), startMatch, endMatch, offset, s.size(), returnedMatchIndex ); } int getNumMatches(); diff --git a/include/eepp/ui/doc/textdocument.hpp b/include/eepp/ui/doc/textdocument.hpp index 45407f9a0..aa49668a9 100644 --- a/include/eepp/ui/doc/textdocument.hpp +++ b/include/eepp/ui/doc/textdocument.hpp @@ -242,9 +242,9 @@ class EE_API TextDocument { String getIndentString(); - const Uint32& getTabWidth() const; + const Uint32& getIndentWidth() const; - void setTabWidth( const Uint32& tabWidth ); + void setIndentWidth( const Uint32& tabWidth ); TextPosition sanitizePosition( const TextPosition& position ) const; @@ -284,6 +284,10 @@ class EE_API TextDocument { void resetSyntax(); + bool getAutoDetectIndentType() const; + + void setAutoDetectIndentType( bool autodetect ); + protected: friend class UndoStack; UndoStack mUndoStack; @@ -293,7 +297,8 @@ class EE_API TextDocument { std::unordered_set mClients; bool mIsCLRF{false}; bool mIsBOM{false}; - Uint32 mTabWidth{4}; + bool mAutoDetectIndentType{true}; + Uint32 mIndentWidth{4}; IndentType mIndentType{IndentTabs}; Clock mTimer; SyntaxDefinition mSyntaxDefinition; @@ -329,6 +334,8 @@ class EE_API TextDocument { TextPosition insert( TextPosition position, const String::StringBaseType& text ); void appendLineIfLastLine( Int64 line ); + + void guessIndentType(); }; }}} // namespace EE::UI::Doc diff --git a/projects/linux/ee.files b/projects/linux/ee.files index b22f377fa..3a2b16c44 100644 --- a/projects/linux/ee.files +++ b/projects/linux/ee.files @@ -1,4 +1,5 @@ ../../README.md +../../TODO.md ../../bin/assets/colorschemes/colorschemes.conf ../../bin/assets/ee.ini ../../bin/assets/layouts/imported.css diff --git a/src/eepp/system/luapatternmatcher.cpp b/src/eepp/system/luapatternmatcher.cpp index 467b1b717..2f3017f43 100644 --- a/src/eepp/system/luapatternmatcher.cpp +++ b/src/eepp/system/luapatternmatcher.cpp @@ -13,6 +13,14 @@ static void failHandler( const char* msg ) { throw std::string( msg ); } +std::string LuaPatternMatcher::match( const std::string& string, const std::string& pattern ) { + LuaPatternMatcher matcher( pattern ); + int start = 0, end = 0; + if ( matcher.find( string, start, end ) ) + return string.substr( start, end - start ); + return ""; +} + LuaPatternMatcher::LuaPatternMatcher( const std::string& pattern ) : mPattern( pattern ) { if ( !sFailHandlerInitialized ) { sFailHandlerInitialized = true; @@ -21,15 +29,15 @@ LuaPatternMatcher::LuaPatternMatcher( const std::string& pattern ) : mPattern( p } bool LuaPatternMatcher::matches( const char* stringSearch, int stringStartOffset, - LuaPatternMatcher::Match* matches, size_t stringLength ) { + LuaPatternMatcher::Match* matchList, size_t stringLength ) { LuaPatternMatcher::Match matchesBuffer[MAX_DEFAULT_MATCHES]; - if ( matches == NULL ) - matches = matchesBuffer; + if ( matchList == NULL ) + matchList = matchesBuffer; if ( stringLength == 0 ) stringLength = strlen( stringSearch ); try { mMatchNum = lua_str_match( stringSearch, stringStartOffset, stringLength, mPattern.c_str(), - (LuaMatch*)matches ); + (LuaMatch*)matchList ); } catch ( const std::string& patternError ) { mErr = std::move( patternError ); mMatchNum = 0; @@ -37,8 +45,8 @@ bool LuaPatternMatcher::matches( const char* stringSearch, int stringStartOffset return mMatchNum == 0 ? false : true; } -bool LuaPatternMatcher::find( const char* stringSearch, int stringStartOffset, int& startMatch, - int& endMatch, int stringLength, int returnMatchIndex ) { +bool LuaPatternMatcher::find( const char* stringSearch, int& startMatch, int& endMatch, + int stringStartOffset, int stringLength, int returnMatchIndex ) { LuaPatternMatcher::Match matchesBuffer[MAX_DEFAULT_MATCHES]; if ( matches( stringSearch, stringStartOffset, matchesBuffer, stringLength ) ) { range( returnMatchIndex, startMatch, endMatch, matchesBuffer ); @@ -49,6 +57,7 @@ bool LuaPatternMatcher::find( const char* stringSearch, int stringStartOffset, i return false; } } + bool LuaPatternMatcher::range( int indexGet, int& startMatch, int& endMatch, LuaPatternMatcher::Match* returnedMatched ) { if ( indexGet == -1 ) { diff --git a/src/eepp/ui/doc/syntaxdefinitionmanager.cpp b/src/eepp/ui/doc/syntaxdefinitionmanager.cpp index 79533e03b..d134f66c0 100644 --- a/src/eepp/ui/doc/syntaxdefinitionmanager.cpp +++ b/src/eepp/ui/doc/syntaxdefinitionmanager.cpp @@ -859,7 +859,7 @@ SyntaxDefinitionManager::SyntaxDefinitionManager() { // ini / conf add( {"Config File", - {"%.ini$", "%.conf$", "%.desktop$", "%.service$", "Doxyfile"}, + {"%.ini$", "%.conf$", "%.desktop$", "%.service$", "%.cfg$", "Doxyfile"}, { {{"#[%da-fA-F]+"}, "literal"}, {{"#.-\n"}, "comment"}, @@ -1739,7 +1739,7 @@ SyntaxDefinitionManager::getStyleByExtension( const std::string& filePath ) cons if ( String::startsWith( ext, "%." ) || String::endsWith( ext, "$" ) ) { LuaPatternMatcher words( ext ); int start, end; - if ( words.find( filePath, 0, start, end ) ) { + if ( words.find( filePath, start, end ) ) { return *style; } } else if ( extension == ext ) { @@ -1758,7 +1758,7 @@ SyntaxDefinitionManager::getStyleByHeader( const std::string& header ) const { for ( auto hdr : style->getHeaders() ) { LuaPatternMatcher words( hdr ); int start, end; - if ( words.find( header, 0, start, end ) ) { + if ( words.find( header, start, end ) ) { return *style; } } diff --git a/src/eepp/ui/doc/syntaxtokenizer.cpp b/src/eepp/ui/doc/syntaxtokenizer.cpp index 03912d1d4..d0370a581 100644 --- a/src/eepp/ui/doc/syntaxtokenizer.cpp +++ b/src/eepp/ui/doc/syntaxtokenizer.cpp @@ -44,7 +44,7 @@ std::pair findNonEscaped( const std::string& text, const std::string& while ( true ) { LuaPatternMatcher words( pattern ); int start, end; - if ( words.find( text, offset, start, end ) ) { + if ( words.find( text, start, end, offset ) ) { if ( !escapeStr.empty() && isScaped( text, start, escapeStr ) ) { offset = end; } else { @@ -95,7 +95,7 @@ std::pair, int> SyntaxTokenizer::tokenize( const Syntax pattern.patterns[0][0] == '^' ? pattern.patterns[0] : "^" + pattern.patterns[0] ); LuaPatternMatcher words( patternStr ); int start, end = 0; - if ( words.find( text, i, start, end ) ) { + if ( words.find( text, start, end, i ) ) { std::string patternText( text.substr( start, end - start ) ); std::string type = syntax.getSymbol( patternText ); pushToken( tokens, type.empty() ? pattern.type : type, patternText ); diff --git a/src/eepp/ui/doc/textdocument.cpp b/src/eepp/ui/doc/textdocument.cpp index d0694fe32..b85694e60 100644 --- a/src/eepp/ui/doc/textdocument.cpp +++ b/src/eepp/ui/doc/textdocument.cpp @@ -1,8 +1,10 @@ +#include #include #include #include #include #include +#include #include #include #include @@ -133,6 +135,9 @@ bool TextDocument::loadFromStream( IOStream& file ) { mLines.push_back( String( "\n" ) ); } + if ( mAutoDetectIndentType ) + guessIndentType(); + notifyTextChanged(); eePRINTL( "Document \"%s\" loaded in %.2fms.", @@ -141,11 +146,55 @@ bool TextDocument::loadFromStream( IOStream& file ) { return true; } +void TextDocument::guessIndentType() { + int guessSpaces = 0; + int guessTabs = 0; + std::map guessWidth; + int guessCoundown = 10; + size_t linesCount = eemin( 100ul, mLines.size() ); + for ( size_t i = 0; i < linesCount; i++ ) { + const String& text = mLines[i].getText(); + std::string match = + LuaPatternMatcher::match( text.size() > 128 ? text.substr( 0, 12 ) : text, "^ +" ); + if ( !match.empty() ) { + guessSpaces++; + guessWidth[match.size()]++; + guessCoundown--; + } else { + match = LuaPatternMatcher::match( mLines[i].getText(), "^\t+" ); + if ( !match.empty() ) { + guessTabs++; + guessCoundown--; + break; // if tab found asume tabs + } + } + if ( guessCoundown == 0 ) + break; + } + if ( !guessTabs && !guessSpaces ) { + return; + } + if ( guessTabs > guessSpaces ) { + mIndentType = IndentTabs; + } else { + mIndentType = IndentSpaces; + mIndentWidth = guessWidth.begin()->first; + } +} + void TextDocument::resetSyntax() { String header( getText( {{0, 0}, positionOffset( {0, 0}, 128 )} ) ); mSyntaxDefinition = SyntaxDefinitionManager::instance()->find( mFilePath, header ); } +bool TextDocument::getAutoDetectIndentType() const { + return mAutoDetectIndentType; +} + +void TextDocument::setAutoDetectIndentType( bool autodetect ) { + mAutoDetectIndentType = autodetect; +} + bool TextDocument::loadFromFile( const std::string& path ) { if ( !FileSystem::fileExists( path ) && PackManager::instance()->isFallbackToPacksActive() ) { std::string pathFix( path ); @@ -903,17 +952,17 @@ void TextDocument::appendLineIfLastLine( Int64 line ) { String TextDocument::getIndentString() { if ( IndentSpaces == mIndentType ) { - return String( std::string( mTabWidth, ' ' ) ); + return String( std::string( mIndentWidth, ' ' ) ); } return String( "\t" ); } -const Uint32& TextDocument::getTabWidth() const { - return mTabWidth; +const Uint32& TextDocument::getIndentWidth() const { + return mIndentWidth; } -void TextDocument::setTabWidth( const Uint32& tabWidth ) { - mTabWidth = tabWidth; +void TextDocument::setIndentWidth( const Uint32& tabWidth ) { + mIndentWidth = tabWidth; } void TextDocument::deleteTo( TextPosition position ) { diff --git a/src/eepp/ui/uicodeeditor.cpp b/src/eepp/ui/uicodeeditor.cpp index 24b03457e..47a804a7c 100644 --- a/src/eepp/ui/uicodeeditor.cpp +++ b/src/eepp/ui/uicodeeditor.cpp @@ -343,7 +343,6 @@ const Uint32& UICodeEditor::getTabWidth() const { UICodeEditor* UICodeEditor::setTabWidth( const Uint32& tabWidth ) { if ( mTabWidth != tabWidth ) { mTabWidth = tabWidth; - mDoc.setTabWidth( mTabWidth ); } return this; } diff --git a/src/eepp/ui/uilistboxitem.cpp b/src/eepp/ui/uilistboxitem.cpp index cac4c6176..1458e6ffc 100644 --- a/src/eepp/ui/uilistboxitem.cpp +++ b/src/eepp/ui/uilistboxitem.cpp @@ -96,7 +96,8 @@ void UIListBoxItem::select() { mNodeFlags |= NODE_FLAG_SELECTED; - if ( NULL != LBParent->mItems[LBParent->mSelected.front()] && + if ( !LBParent->mSelected.empty() && + NULL != LBParent->mItems[LBParent->mSelected.front()] && LBParent->getItemIndex( this ) != LBParent->mSelected.front() ) { LBParent->mItems[LBParent->mSelected.front()]->unselect(); }