From 396f1e2558098e1cd3e26dc388d6624aac962911 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mart=C3=ADn=20Lucas=20Golini?= Date: Mon, 27 May 2024 00:57:18 -0300 Subject: [PATCH] Code Folding WIP. --- include/eepp/ui/doc/documentview.hpp | 11 +- include/eepp/ui/doc/foldrangeservice.hpp | 52 +++++ include/eepp/ui/doc/foldrangetype.hpp | 10 + include/eepp/ui/doc/syntaxdefinition.hpp | 11 + include/eepp/ui/doc/textdocument.hpp | 6 + projects/linux/ee.files | 3 + src/eepp/ui/doc/documentview.cpp | 217 +++++++++--------- src/eepp/ui/doc/foldrangeservice.cpp | 78 +++++++ src/eepp/ui/doc/languages/c.cpp | 4 +- src/eepp/ui/doc/languages/cpp.cpp | 4 +- src/eepp/ui/doc/languages/javascript.cpp | 4 +- src/eepp/ui/doc/languages/typescript.cpp | 7 +- src/eepp/ui/doc/syntaxdefinition.cpp | 21 +- src/eepp/ui/doc/textdocument.cpp | 11 +- src/eepp/ui/uicodeeditor.cpp | 69 ++++-- .../ecode/plugins/lsp/lspclientplugin.cpp | 34 +++ .../ecode/plugins/lsp/lspclientplugin.hpp | 4 +- .../ecode/plugins/lsp/lspclientserver.cpp | 49 ++++ .../ecode/plugins/lsp/lspclientserver.hpp | 8 + .../ecode/plugins/lsp/lspdocumentclient.cpp | 52 +++++ .../ecode/plugins/lsp/lspdocumentclient.hpp | 5 + src/tools/ecode/plugins/lsp/lspprotocol.hpp | 14 ++ src/tools/ecode/plugins/pluginmanager.hpp | 6 +- 23 files changed, 541 insertions(+), 139 deletions(-) create mode 100644 include/eepp/ui/doc/foldrangeservice.hpp create mode 100644 include/eepp/ui/doc/foldrangetype.hpp create mode 100644 src/eepp/ui/doc/foldrangeservice.cpp diff --git a/include/eepp/ui/doc/documentview.hpp b/include/eepp/ui/doc/documentview.hpp index e3a1fc225..986039672 100644 --- a/include/eepp/ui/doc/documentview.hpp +++ b/include/eepp/ui/doc/documentview.hpp @@ -131,11 +131,7 @@ class EE_API DocumentView { bool isLineVisible( Int64 docIdx ) const; - bool isFolded( Int64 docIdx ) const; - - void addFoldRegion( TextRange region ); - - bool isFoldingRegionInLine( Int64 docIdx ); + bool isFolded( Int64 docIdx, bool andNotFirstLine = false ) const; void foldRegion( Int64 foldDocIdx ); @@ -151,7 +147,6 @@ class EE_API DocumentView { std::vector mVisibleLines; std::vector mVisibleLinesOffset; std::vector mDocLineToVisibleIndex; - std::unordered_map mFoldingRegions; std::vector mFoldedRegions; bool mPendingReconstruction{ false }; bool mUnderConstruction{ false }; @@ -161,6 +156,10 @@ class EE_API DocumentView { void removeFoldedRegion( const TextRange& region ); void shiftFoldingRegions( Int64 fromLine, Int64 numLines ); + + void verifyStructuralConsistency(); + + void recomputeDocLineToVisibleIndex( Int64 fromVisibleIndex ); }; }}} // namespace EE::UI::Doc diff --git a/include/eepp/ui/doc/foldrangeservice.hpp b/include/eepp/ui/doc/foldrangeservice.hpp new file mode 100644 index 000000000..6fca639c4 --- /dev/null +++ b/include/eepp/ui/doc/foldrangeservice.hpp @@ -0,0 +1,52 @@ +#ifndef EE_UI_DOC_FOLDRANGESERVICE_HPP +#define EE_UI_DOC_FOLDRANGESERVICE_HPP + +#include +#include +#include +#include +#include +#include + +using namespace EE::System; + +namespace EE { namespace UI { namespace Doc { + +class TextDocument; + +class EE_API FoldRangeServive { + public: + using FoldRangeProvider = std::function; + + FoldRangeServive( TextDocument* doc ); + + void findRegions(); + + void clear(); + + std::optional find( Int64 docIdx ); + + void addFoldRegion( TextRange region ); + + bool isFoldingRegionInLine( Int64 docIdx ); + + void removeFoldedRegion( const TextRange& region ); + + void shiftFoldingRegions( Int64 fromLine, Int64 numLines ); + + void setFoldingRegions( std::vector regions ); + + const FoldRangeProvider& getProvider() const; + + void setProvider( const FoldRangeProvider& provider ); + + protected: + TextDocument* mDoc; + std::unordered_map mFoldingRegions; + FoldRangeProvider mProvider; + Mutex mMutex; +}; + +}}} // namespace EE::UI::Doc + +#endif // EE_UI_DOC_FOLDRANGESERVICE_HPP diff --git a/include/eepp/ui/doc/foldrangetype.hpp b/include/eepp/ui/doc/foldrangetype.hpp new file mode 100644 index 000000000..6d2874f87 --- /dev/null +++ b/include/eepp/ui/doc/foldrangetype.hpp @@ -0,0 +1,10 @@ +#ifndef EE_UI_DOC_FOLDRANGETYPE_HPP +#define EE_UI_DOC_FOLDRANGETYPE_HPP + +namespace EE { namespace UI { namespace Doc { + +enum class FoldRangeType { Braces, Indentation, Tag, Undefined }; + +}}} // namespace EE::UI::Doc + +#endif // EE_UI_DOC_FOLDRANGETYPE_HPP diff --git a/include/eepp/ui/doc/syntaxdefinition.hpp b/include/eepp/ui/doc/syntaxdefinition.hpp index 510dfa946..169cb83e3 100644 --- a/include/eepp/ui/doc/syntaxdefinition.hpp +++ b/include/eepp/ui/doc/syntaxdefinition.hpp @@ -3,6 +3,7 @@ #include #include +#include #include #include #include @@ -139,6 +140,14 @@ class EE_API SyntaxDefinition { SyntaxDefinition& setCaseInsensitive( bool caseInsensitive ); + FoldRangeType getFoldRangeType() const; + + SyntaxDefinition& setFoldRangeType( FoldRangeType foldRangeType ); + + std::vector> getFoldBraces() const; + + SyntaxDefinition& setFoldBraces( const std::vector>& foldBraces ); + protected: friend class SyntaxDefinitionManager; @@ -152,6 +161,8 @@ class EE_API SyntaxDefinition { std::vector mHeaders; std::string mLSPName; Uint16 mLanguageIndex{ 0 }; + FoldRangeType mFoldRangeType{ FoldRangeType::Undefined }; + std::vector> mFoldBraces; bool mAutoCloseXMLTags{ false }; bool mVisible{ true }; bool mHasExtensionPriority{ false }; diff --git a/include/eepp/ui/doc/textdocument.hpp b/include/eepp/ui/doc/textdocument.hpp index 0cbb821ad..a1363b0ae 100644 --- a/include/eepp/ui/doc/textdocument.hpp +++ b/include/eepp/ui/doc/textdocument.hpp @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -619,6 +620,10 @@ class EE_API TextDocument { void setEncoding( TextFormat::Encoding encoding ); + const FoldRangeServive& getFoldRangeService() const; + + FoldRangeServive& getFoldRangeService(); + protected: friend class TextUndoStack; @@ -671,6 +676,7 @@ class EE_API TextDocument { std::unique_ptr mHighlighter; Mutex mStopFlagsMutex; UnorderedMap> mStopFlags; + FoldRangeServive mFoldRangeService; void initializeCommands(); diff --git a/projects/linux/ee.files b/projects/linux/ee.files index f62334ddd..48ff6c664 100644 --- a/projects/linux/ee.files +++ b/projects/linux/ee.files @@ -303,6 +303,7 @@ ../../include/eepp/thirdparty/chipmunk/cpSpatialIndex.h ../../include/eepp/thirdparty/chipmunk/cpVect.h ../../include/eepp/ui/doc/documentview.hpp +../../include/eepp/ui/doc/foldrangeservice.hpp ../../include/eepp/ui/models/model.hpp ../../include/eepp/ui/abstract/uiabstracttableview.hpp ../../include/eepp/ui/abstract/uiabstractview.hpp @@ -335,6 +336,7 @@ ../../include/eepp/ui/css/stylesheetvariable.hpp ../../include/eepp/ui/css/timingfunction.hpp ../../include/eepp/ui/css/transitiondefinition.hpp +../../include/eepp/ui/doc/foldrangetype.hpp ../../include/eepp/ui/doc/syntaxcolorscheme.hpp ../../include/eepp/ui/doc/syntaxdefinition.hpp ../../include/eepp/ui/doc/syntaxdefinitionmanager.hpp @@ -862,6 +864,7 @@ ../../src/eepp/system/virtualfilesystem.cpp ../../src/eepp/system/zip.cpp ../../src/eepp/ui/abstract/filesystemmodel.hpp +../../src/eepp/ui/doc/foldrangeservice.cpp ../../src/eepp/ui/doc/languages/adept.cpp ../../src/eepp/ui/doc/languages/adept.hpp ../../src/eepp/ui/doc/languages/angelscript.cpp diff --git a/src/eepp/ui/doc/documentview.cpp b/src/eepp/ui/doc/documentview.cpp index bb200f740..df2c2fa05 100644 --- a/src/eepp/ui/doc/documentview.cpp +++ b/src/eepp/ui/doc/documentview.cpp @@ -1,4 +1,5 @@ #include +#include #include #include #include @@ -202,6 +203,7 @@ void DocumentView::invalidateCache() { return; } + Clock clock; BoolScopedOp op( mUnderConstruction, true ); mVisibleLines.clear(); @@ -215,7 +217,7 @@ void DocumentView::invalidateCache() { mDocLineToVisibleIndex.reserve( linesCount ); for ( auto i = 0; i < linesCount; i++ ) { - if ( isFolded( i ) ) { + if ( isFolded( i, true ) ) { mVisibleLinesOffset.emplace_back( wrap ? computeOffsets( mDoc->line( i ).getText().view(), mFontStyle, mConfig.tabWidth ) @@ -240,6 +242,9 @@ void DocumentView::invalidateCache() { eeASSERT( static_cast( mDocLineToVisibleIndex.size() ) == linesCount ); mPendingReconstruction = false; + + Log::debug( "DocumentView for \"%s\" generated in %s", mDoc->getFilePath(), + clock.getElapsedTime().toString() ); } VisibleIndex DocumentView::toVisibleIndex( Int64 docIdx, bool retLast ) const { @@ -359,8 +364,8 @@ void DocumentView::clearCache() { void DocumentView::clear() { clearCache(); - mFoldingRegions.clear(); mFoldedRegions.clear(); + mDoc->getFoldRangeService().clear(); } Float DocumentView::getLineYOffset( VisibleIndex visibleIndex, Float lineHeight ) const { @@ -419,11 +424,17 @@ void DocumentView::updateCache( Int64 fromLine, Int64 toLine, Int64 numLines ) { } } + recomputeDocLineToVisibleIndex( oldIdxFrom ); + + verifyStructuralConsistency(); +} + +void DocumentView::recomputeDocLineToVisibleIndex( Int64 fromVisibleIndex ) { // Recompute document line to visible index Int64 visibleLinesCount = mVisibleLines.size(); mDocLineToVisibleIndex.resize( mDoc->linesCount() ); - Int64 previousLineIdx = mVisibleLines[oldIdxFrom].line(); - for ( Int64 visibleIdx = oldIdxFrom; visibleIdx < visibleLinesCount; visibleIdx++ ) { + Int64 previousLineIdx = mVisibleLines[fromVisibleIndex].line(); + for ( Int64 visibleIdx = fromVisibleIndex; visibleIdx < visibleLinesCount; visibleIdx++ ) { const auto& visibleLine = mVisibleLines[visibleIdx]; if ( visibleLine.column() == 0 ) { // Non-contiguos lines means hidden lines @@ -435,7 +446,104 @@ void DocumentView::updateCache( Int64 fromLine, Int64 toLine, Int64 numLines ) { previousLineIdx = visibleLine.line(); } } +} +size_t DocumentView::getVisibleLinesCount() const { + return isOneToOne() ? mDoc->linesCount() : mVisibleLines.size(); +} + +void DocumentView::foldRegion( Int64 foldDocIdx ) { + auto foldRegion = mDoc->getFoldRangeService().find( foldDocIdx ); + if ( !foldRegion ) + return; + Int64 toDocIdx = foldRegion->end().line(); + bool foldWasEmpty = mFoldedRegions.empty(); + changeVisibility( foldDocIdx + 1, toDocIdx, false ); + mFoldedRegions.push_back( *foldRegion ); + std::sort( mFoldedRegions.begin(), mFoldedRegions.end() ); + verifyStructuralConsistency(); + if ( foldWasEmpty && mConfig.mode == LineWrapMode::NoWrap ) + invalidateCache(); +} + +void DocumentView::unfoldRegion( Int64 foldDocIdx ) { + auto foldRegion = mDoc->getFoldRangeService().find( foldDocIdx ); + if ( !foldRegion ) + return; + Int64 toDocIdx = foldRegion->end().line(); + changeVisibility( foldDocIdx + 1, toDocIdx, true ); + removeFoldedRegion( *foldRegion ); + verifyStructuralConsistency(); + if ( isOneToOne() ) + clearCache(); +} + +bool DocumentView::isOneToOne() const { + return mConfig.mode == LineWrapMode::NoWrap && mFoldedRegions.empty(); +} + +void DocumentView::changeVisibility( Int64 fromDocIdx, Int64 toDocIdx, bool visible ) { + if ( visible ) { + + auto it = std::lower_bound( mVisibleLines.begin(), mVisibleLines.end(), + TextPosition{ fromDocIdx, 0 } ); + Int64 oldIdxFrom = std::distance( mVisibleLines.begin(), it ); + auto idxOffset = oldIdxFrom; + for ( auto i = fromDocIdx; i <= toDocIdx; i++ ) { + auto lb = isWrapEnabled() + ? computeLineBreaks( *mDoc, i, mFontStyle, mMaxWidth, mConfig.mode, + mConfig.keepIndentation, mConfig.tabWidth ) + : LineWrapInfo{ { 0 }, 0 }; + mVisibleLinesOffset[i] = lb.paddingStart; + for ( const auto& col : lb.wraps ) { + mVisibleLines.insert( mVisibleLines.begin() + idxOffset, { i, col } ); + idxOffset++; + } + } + + recomputeDocLineToVisibleIndex( oldIdxFrom ); + } else { + Int64 oldIdxFrom = static_cast( toVisibleIndex( fromDocIdx ) ); + Int64 oldIdxTo = static_cast( toVisibleIndex( toDocIdx, true ) ); + mVisibleLines.erase( mVisibleLines.begin() + oldIdxFrom, + mVisibleLines.begin() + oldIdxTo + 1 ); + for ( Int64 idx = fromDocIdx; idx <= toDocIdx; idx++ ) { + mDocLineToVisibleIndex[idx] = static_cast( VisibleIndex::invalid ); + } + Int64 linesCount = mDoc->linesCount(); + Int64 idxOffset = oldIdxTo - oldIdxFrom + 1; + for ( Int64 idx = toDocIdx + 1; idx < linesCount; idx++ ) + mDocLineToVisibleIndex[idx] -= idxOffset; + } + eeASSERT( mDocLineToVisibleIndex.size() == mDoc->linesCount() ); +} + +bool DocumentView::isFolded( Int64 docIdx, bool andNotFirstLine ) const { + return std::any_of( mFoldedRegions.begin(), mFoldedRegions.end(), + [&]( const TextRange& region ) { + return region.containsLine( docIdx ) && + ( andNotFirstLine ? region.start().line() != docIdx : true ); + } ); +} + +void DocumentView::removeFoldedRegion( const TextRange& region ) { + auto found = std::find( mFoldedRegions.begin(), mFoldedRegions.end(), region ); + if ( found != mFoldedRegions.end() ) + mFoldedRegions.erase( found ); +} + +void DocumentView::shiftFoldingRegions( Int64 fromLine, Int64 numLines ) { + mDoc->getFoldRangeService().shiftFoldingRegions( fromLine, numLines ); + + for ( auto& region : mFoldedRegions ) { + if ( region.start().line() >= fromLine ) { + region.start().setLine( region.start().line() + numLines ); + region.end().setLine( region.end().line() + numLines ); + } + } +} + +void DocumentView::verifyStructuralConsistency() { #ifdef EE_DEBUG auto visibleLines = mVisibleLines; auto docLineToVisibleIndex = mDocLineToVisibleIndex; @@ -449,105 +557,4 @@ void DocumentView::updateCache( Int64 fromLine, Int64 toLine, Int64 numLines ) { #endif } -size_t DocumentView::getVisibleLinesCount() const { - return isOneToOne() ? mDoc->linesCount() : mVisibleLines.size(); -} - -void DocumentView::addFoldRegion( TextRange region ) { - region.normalize(); - mFoldingRegions[region.start().line()] = std::move( region ); -} - -bool DocumentView::isFoldingRegionInLine( Int64 docIdx ) { - auto foldRegionIt = mFoldingRegions.find( docIdx ); - return foldRegionIt != mFoldingRegions.end(); -} - -void DocumentView::foldRegion( Int64 foldDocIdx ) { - auto foldRegionIt = mFoldingRegions.find( foldDocIdx ); - if ( foldRegionIt == mFoldingRegions.end() ) - return; - Int64 toDocIdx = foldRegionIt->second.end().line(); - changeVisibility( foldDocIdx, toDocIdx, false ); - bool foldWasEmpty = mFoldedRegions.empty(); - mFoldedRegions.push_back( foldRegionIt->second ); - std::sort( mFoldedRegions.begin(), mFoldedRegions.end() ); - if ( foldWasEmpty && mConfig.mode == LineWrapMode::NoWrap ) - invalidateCache(); -} - -void DocumentView::unfoldRegion( Int64 foldDocIdx ) { - auto foldRegionIt = mFoldingRegions.find( foldDocIdx ); - if ( foldRegionIt == mFoldingRegions.end() ) - return; - Int64 toDocIdx = foldRegionIt->second.end().line(); - changeVisibility( foldDocIdx, toDocIdx, true ); - removeFoldedRegion( foldRegionIt->second ); - if ( isOneToOne() ) - clearCache(); -} - -bool DocumentView::isOneToOne() const { - return mConfig.mode == LineWrapMode::NoWrap && mFoldedRegions.empty(); -} - -void DocumentView::changeVisibility( Int64 fromDocIdx, Int64 toDocIdx, bool visible ) { - if ( visible ) { - auto it = std::lower_bound( mVisibleLines.begin(), mVisibleLines.end(), - TextPosition{ fromDocIdx, 0 } ); - auto idxOffset = std::distance( mVisibleLines.begin(), it ); - for ( auto i = fromDocIdx; i <= toDocIdx; i++ ) { - auto lb = isWrapEnabled() - ? computeLineBreaks( *mDoc, i, mFontStyle, mMaxWidth, mConfig.mode, - mConfig.keepIndentation, mConfig.tabWidth ) - : LineWrapInfo{ { 0 }, 0 }; - mVisibleLinesOffset[i] = lb.paddingStart; - for ( const auto& col : lb.wraps ) { - mVisibleLines.insert( mVisibleLines.begin() + idxOffset, { i, col } ); - idxOffset++; - } - } - } else { - Int64 oldIdxFrom = static_cast( toVisibleIndex( fromDocIdx ) ); - Int64 oldIdxTo = static_cast( toVisibleIndex( toDocIdx, true ) ); - mVisibleLines.erase( mVisibleLines.begin() + oldIdxFrom, - mVisibleLines.begin() + oldIdxTo + 1 ); - for ( Int64 idx = fromDocIdx; idx <= toDocIdx; idx++ ) { - mDocLineToVisibleIndex[idx] = static_cast( VisibleIndex::invalid ); - } - Int64 linesCount = mDoc->linesCount(); - Int64 idxOffset = oldIdxTo - oldIdxFrom + 1; - for ( Int64 idx = toDocIdx + 1; idx < linesCount; idx++ ) - mDocLineToVisibleIndex[idx] -= idxOffset; - eeASSERT( mDocLineToVisibleIndex.size() == mDoc->linesCount() ); - } -} - -void DocumentView::removeFoldedRegion( const TextRange& region ) { - auto found = std::find( mFoldedRegions.begin(), mFoldedRegions.end(), region ); - if ( found != mFoldedRegions.end() ) - mFoldedRegions.erase( found ); -} - -bool DocumentView::isFolded( Int64 docIdx ) const { - return std::any_of( - mFoldedRegions.begin(), mFoldedRegions.end(), - [docIdx]( const TextRange& region ) { return region.containsLine( docIdx ); } ); -} - -void DocumentView::shiftFoldingRegions( Int64 fromLine, Int64 numLines ) { - for ( auto& [_, region] : mFoldingRegions ) { - if ( region.start().line() >= fromLine ) { - region.start().setLine( region.start().line() + numLines ); - region.end().setLine( region.end().line() + numLines ); - } - } - for ( auto& region : mFoldedRegions ) { - if ( region.start().line() >= fromLine ) { - region.start().setLine( region.start().line() + numLines ); - region.end().setLine( region.end().line() + numLines ); - } - } -} - }}} // namespace EE::UI::Doc diff --git a/src/eepp/ui/doc/foldrangeservice.cpp b/src/eepp/ui/doc/foldrangeservice.cpp new file mode 100644 index 000000000..e2f833741 --- /dev/null +++ b/src/eepp/ui/doc/foldrangeservice.cpp @@ -0,0 +1,78 @@ +#include +#include + +namespace EE { namespace UI { namespace Doc { + +FoldRangeServive::FoldRangeServive( TextDocument* doc ) : mDoc( doc ) {} + +void FoldRangeServive::findRegions() { + if ( mDoc == nullptr ) + return; + + if ( mProvider && mProvider( mDoc ) ) + return; + + switch ( mDoc->getSyntaxDefinition().getFoldRangeType() ) { + case FoldRangeType::Braces: + break; + case FoldRangeType::Indentation: + case FoldRangeType::Tag: + case FoldRangeType::Undefined: + break; + } +} + +void FoldRangeServive::clear() { + Lock l( mMutex ); + mFoldingRegions.clear(); +} + +std::optional FoldRangeServive::find( Int64 docIdx ) { + Lock l( mMutex ); + auto foldRegionIt = mFoldingRegions.find( docIdx ); + if ( foldRegionIt == mFoldingRegions.end() ) + return {}; + return foldRegionIt->second; +} + +void FoldRangeServive::addFoldRegion( TextRange region ) { + Lock l( mMutex ); + region.normalize(); + mFoldingRegions[region.start().line()] = std::move( region ); +} + +bool FoldRangeServive::isFoldingRegionInLine( Int64 docIdx ) { + Lock l( mMutex ); + auto foldRegionIt = mFoldingRegions.find( docIdx ); + return foldRegionIt != mFoldingRegions.end(); +} + +void FoldRangeServive::shiftFoldingRegions( Int64 fromLine, Int64 numLines ) { + Lock l( mMutex ); + for ( auto& [_, region] : mFoldingRegions ) { + if ( region.start().line() >= fromLine ) { + region.start().setLine( region.start().line() + numLines ); + region.end().setLine( region.end().line() + numLines ); + } + } +} + +void FoldRangeServive::setFoldingRegions( std::vector regions ) { + Lock l( mMutex ); + mFoldingRegions.clear(); + std::sort( regions.begin(), regions.end() ); + for ( auto& range : regions ) { + auto line = range.start().line(); + mFoldingRegions[line] = std::move( range ); + } +} + +const FoldRangeServive::FoldRangeProvider& FoldRangeServive::getProvider() const { + return mProvider; +} + +void FoldRangeServive::setProvider( const FoldRangeProvider& provider ) { + mProvider = provider; +} + +}}} // namespace EE::UI::Doc diff --git a/src/eepp/ui/doc/languages/c.cpp b/src/eepp/ui/doc/languages/c.cpp index f360b2641..0342ecb79 100644 --- a/src/eepp/ui/doc/languages/c.cpp +++ b/src/eepp/ui/doc/languages/c.cpp @@ -5,7 +5,7 @@ namespace EE { namespace UI { namespace Doc { namespace Language { void addC() { - SyntaxDefinitionManager::instance()->add( + auto& sd = SyntaxDefinitionManager::instance()->add( { "C", { "%.c$", "%.C", "%.h$", "%.icc" }, @@ -50,6 +50,8 @@ void addC() { {} } ); + + sd.setFoldRangeType( FoldRangeType::Braces ).setFoldBraces( { { '{', '}' } } ); } }}}} // namespace EE::UI::Doc::Language diff --git a/src/eepp/ui/doc/languages/cpp.cpp b/src/eepp/ui/doc/languages/cpp.cpp index cec5e0e6a..112693821 100644 --- a/src/eepp/ui/doc/languages/cpp.cpp +++ b/src/eepp/ui/doc/languages/cpp.cpp @@ -5,7 +5,7 @@ namespace EE { namespace UI { namespace Doc { namespace Language { void addCPP() { - SyntaxDefinitionManager::instance()->add( + auto& sd = SyntaxDefinitionManager::instance()->add( { "C++", { "%.cpp$", "%.cc$", "%.cxx$", "%.c++$", "%.hh$", "%.inl$", "%.hxx$", "%.hpp$", "%.h++$", @@ -101,6 +101,8 @@ void addCPP() { "//", {}, "cpp" } ); + + sd.setFoldRangeType( FoldRangeType::Braces ).setFoldBraces( { { '{', '}' } } ); } }}}} // namespace EE::UI::Doc::Language diff --git a/src/eepp/ui/doc/languages/javascript.cpp b/src/eepp/ui/doc/languages/javascript.cpp index 720957e28..da1b0f9e6 100644 --- a/src/eepp/ui/doc/languages/javascript.cpp +++ b/src/eepp/ui/doc/languages/javascript.cpp @@ -5,7 +5,7 @@ namespace EE { namespace UI { namespace Doc { namespace Language { void addJavaScript() { - SyntaxDefinitionManager::instance()->add( + auto& sd = SyntaxDefinitionManager::instance()->add( { "JavaScript", { "%.js$" }, @@ -60,6 +60,8 @@ void addJavaScript() { {} } ); + + sd.setFoldRangeType( FoldRangeType::Braces ).setFoldBraces( { { '{', '}' } } ); } }}}} // namespace EE::UI::Doc::Language diff --git a/src/eepp/ui/doc/languages/typescript.cpp b/src/eepp/ui/doc/languages/typescript.cpp index d2d8a3cc6..629b17111 100644 --- a/src/eepp/ui/doc/languages/typescript.cpp +++ b/src/eepp/ui/doc/languages/typescript.cpp @@ -71,6 +71,8 @@ void addTypeScript() { } ); + ts.setFoldRangeType( FoldRangeType::Braces ).setFoldBraces( { { '{', '}' } } ); + SyntaxDefinitionManager::instance() ->add( { "TSX", { "%.tsx$" }, @@ -101,7 +103,10 @@ void addTypeScript() { "//" } ) .setSymbols( ts.getSymbols() ) .setLSPName( "typescriptreact" ) - .setAutoCloseXMLTags( true ); + .setAutoCloseXMLTags( true ) + .setFoldRangeType( FoldRangeType::Braces ) + .setFoldBraces( { { '{', '}' } } ); + ; } }}}} // namespace EE::UI::Doc::Language diff --git a/src/eepp/ui/doc/syntaxdefinition.cpp b/src/eepp/ui/doc/syntaxdefinition.cpp index e64c0714c..1507cf65b 100644 --- a/src/eepp/ui/doc/syntaxdefinition.cpp +++ b/src/eepp/ui/doc/syntaxdefinition.cpp @@ -75,7 +75,7 @@ bool SyntaxDefinition::hasExtensionPriority() const { return mHasExtensionPriority; } -SyntaxDefinition& SyntaxDefinition::setExtensionPriority( bool hasExtensionPriority ) { +SyntaxDefinition& SyntaxDefinition::setExtensionPriority( bool hasExtensionPriority ) { mHasExtensionPriority = hasExtensionPriority; return *this; } @@ -93,6 +93,25 @@ SyntaxDefinition& SyntaxDefinition::setCaseInsensitive( bool caseInsensitive ) { return *this; } +FoldRangeType SyntaxDefinition::getFoldRangeType() const { + return mFoldRangeType; +} + +SyntaxDefinition& SyntaxDefinition::setFoldRangeType( FoldRangeType foldRangeType ) { + mFoldRangeType = foldRangeType; + return *this; +} + +std::vector> SyntaxDefinition::getFoldBraces() const { + return mFoldBraces; +} + +SyntaxDefinition& +SyntaxDefinition::setFoldBraces( const std::vector>& foldBraces ) { + mFoldBraces = foldBraces; + return *this; +} + const std::vector& SyntaxDefinition::getPatterns() const { return mPatterns; } diff --git a/src/eepp/ui/doc/textdocument.cpp b/src/eepp/ui/doc/textdocument.cpp index 29167f287..bd38b8c2d 100644 --- a/src/eepp/ui/doc/textdocument.cpp +++ b/src/eepp/ui/doc/textdocument.cpp @@ -37,7 +37,8 @@ TextDocument::TextDocument( bool verbose ) : mDefaultFileName( "untitled" ), mCleanChangeId( 0 ), mNonWordChars( DEFAULT_NON_WORD_CHARS ), - mHighlighter( std::make_unique( this ) ) { + mHighlighter( std::make_unique( this ) ), + mFoldRangeService( this ) { initializeCommands(); reset(); } @@ -2974,6 +2975,14 @@ void TextDocument::setEncoding( TextFormat::Encoding encoding ) { mEncoding = encoding; } +const FoldRangeServive& TextDocument::getFoldRangeService() const { + return mFoldRangeService; +} + +FoldRangeServive& TextDocument::getFoldRangeService() { + return mFoldRangeService; +} + static inline void changeDepth( SyntaxHighlighter* highlighter, int& depth, const TextPosition& pos, int dir ) { if ( highlighter ) { diff --git a/src/eepp/ui/uicodeeditor.cpp b/src/eepp/ui/uicodeeditor.cpp index edb8dfff0..83497bab4 100644 --- a/src/eepp/ui/uicodeeditor.cpp +++ b/src/eepp/ui/uicodeeditor.cpp @@ -124,8 +124,8 @@ UICodeEditor::UICodeEditor( const std::string& elementTag, const bool& autoRegis mTabWidth( 4 ), mMouseWheelScroll( 50 ), mFontSize( mFontStyleConfig.getFontCharacterSize() ), - mLineNumberPaddingLeft( 8 ), - mLineNumberPaddingRight( 8 ), + mLineNumberPaddingLeft( PixelDensity::dpToPx( 6 ) ), + mLineNumberPaddingRight( PixelDensity::dpToPx( 6 ) ), mKeyBindings( getUISceneNode()->getWindow()->getInput() ), mFindLongestLineWidthUpdateFrequency( Seconds( 1 ) ), mPreviewColor( Color::Transparent ) { @@ -529,6 +529,7 @@ bool UICodeEditor::loadAsyncFromURL( mDoc->getHighlighter()->tokenizeAsync( getUISceneNode()->getThreadPool(), [this] { runOnMainThread( [this] { invalidateDraw(); } ); } ); + runOnMainThread( [this, success, onLoaded, wasLocked] { if ( !wasLocked ) setLocked( false ); @@ -1344,28 +1345,37 @@ Uint32 UICodeEditor::onMouseDown( const Vector2i& position, const Uint32& flags Input* input = getUISceneNode()->getWindow()->getInput(); input->captureMouse( true ); setFocus(); + + auto textScreenPos( resolveScreenPosition( position.asFloat() ) ); if ( flags & EE_BUTTON_LMASK ) { - if ( input->isModState( KEYMOD_LALT | KEYMOD_SHIFT ) ) { - TextRange range( mDoc->getSelection().start(), - resolveScreenPosition( position.asFloat() ) ); + Vector2f localPos( convertToNodeSpace( position.asFloat() ) ); + if ( localPos.x < mPaddingPx.Left + getGutterWidth() && + mDoc->getFoldRangeService().isFoldingRegionInLine( textScreenPos.line() ) ) { + if ( mDocView.isFolded( textScreenPos.line() ) ) { + mDocView.unfoldRegion( textScreenPos.line() ); + } else { + mDocView.foldRegion( textScreenPos.line() ); + } + } else if ( input->isModState( KEYMOD_LALT | KEYMOD_SHIFT ) ) { + TextRange range( mDoc->getSelection().start(), textScreenPos ); range.normalize(); range = mDoc->sanitizeRange( range ); for ( Int64 i = range.start().line(); i < range.end().line(); ++i ) mDoc->addSelection( { i, range.start().column() } ); } else if ( input->isModState( KEYMOD_SHIFT ) ) { - mDoc->selectTo( resolveScreenPosition( position.asFloat() ) ); + mDoc->selectTo( textScreenPos ); } else if ( input->isModState( KEYMOD_CTRL ) && checkMouseOverLink( position ).empty() ) { - TextPosition pos( resolveScreenPosition( position.asFloat() ) ); + TextPosition pos( textScreenPos ); if ( !mDoc->selectionExists( pos ) ) mDoc->addSelection( { pos, pos } ); } else if ( mLastDoubleClick.getElapsedTime() < Milliseconds( 300.f ) ) { mDoc->selectLine(); } else { - mDoc->setSelection( resolveScreenPosition( position.asFloat() ) ); + mDoc->setSelection( textScreenPos ); } } else if ( !mDoc->hasSelection() ) { - mDoc->setSelection( resolveScreenPosition( position.asFloat() ) ); + mDoc->setSelection( textScreenPos ); } } return UIWidget::onMouseDown( position, flags ); @@ -3716,17 +3726,38 @@ void UICodeEditor::drawLineNumbers( const DocumentLineRange& lineRange, const Ve } else { pos = String( String::toString( i + 1 ) ).padLeft( lineNumberDigits, ' ' ); } - Text::draw( - pos, + auto lnPos( Vector2f( screenStart.x + mLineNumberPaddingLeft, - startScroll.y + mDocView.getLineYOffset( i, lineHeight ) + lineOffset ), - mFontStyleConfig.Font, fontSize, - ( i >= selection.start().line() && i <= selection.end().line() ) - ? mLineNumberActiveFontColor - : mLineNumberFontColor, - mFontStyleConfig.Style, mFontStyleConfig.OutlineThickness, - mFontStyleConfig.OutlineColor, mFontStyleConfig.ShadowColor, - mFontStyleConfig.ShadowOffset ); + startScroll.y + mDocView.getLineYOffset( i, lineHeight ) + lineOffset ) ); + + Text::draw( pos, lnPos, mFontStyleConfig.Font, fontSize, + ( i >= selection.start().line() && i <= selection.end().line() ) + ? mLineNumberActiveFontColor + : mLineNumberFontColor, + mFontStyleConfig.Style, mFontStyleConfig.OutlineThickness, + mFontStyleConfig.OutlineColor, mFontStyleConfig.ShadowColor, + mFontStyleConfig.ShadowOffset ); + + if ( mDoc->getFoldRangeService().isFoldingRegionInLine( i ) ) { + Float dim = PixelDensity::dpToPx( 4 ); + lnPos = Vector2f( screenStart.x + lineNumberWidth - dim, + lnPos.y + lineHeight * 0.5f - dim * 0.5f ); + primitives.setColor( mLineNumberFontColor ); + Triangle2f tri; + if ( mDocView.isFolded( i ) ) { + tri.V[0] = { 0, 0 }; + tri.V[1] = { 0, dim }; + tri.V[2] = { dim, dim * 0.5f }; + } else { + tri.V[0] = { 0, 0 }; + tri.V[1] = { dim, 0 }; + tri.V[2] = { dim * 0.5f, dim }; + } + tri.V[0] += lnPos; + tri.V[1] += lnPos; + tri.V[2] += lnPos; + primitives.drawTriangle( tri ); + } } } diff --git a/src/tools/ecode/plugins/lsp/lspclientplugin.cpp b/src/tools/ecode/plugins/lsp/lspclientplugin.cpp index 635a61b3c..3cf7478ab 100644 --- a/src/tools/ecode/plugins/lsp/lspclientplugin.cpp +++ b/src/tools/ecode/plugins/lsp/lspclientplugin.cpp @@ -340,6 +340,34 @@ PluginRequestHandle LSPClientPlugin::processTextDocumentSymbol( const PluginMess return { uri.toString() }; } +PluginRequestHandle LSPClientPlugin::processFoldingRanges( const PluginMessage& msg ) { + if ( !msg.isRequest() || msg.type != PluginMessageType::FoldingRanges || + msg.format != PluginMessageFormat::JSON || !msg.asJSON().contains( "uri" ) ) + return {}; + + URI uri( msg.asJSON().value( "uri", "" ) ); + if ( uri.empty() ) + return {}; + + LSPClientServer* server = mClientManager.getOneLSPClientServer( uri ); + if ( !server || !server->getCapabilities().foldingRangeProvider ) + return {}; + + auto handler = [uri, this]( const PluginIDType& id, const std::vector& res ) { + mManager->sendResponse( this, PluginMessageType::FoldingRanges, + PluginMessageFormat::FoldingRanges, &res, id ); + }; + + if ( Engine::instance()->isMainThread() ) { + server->getThreadPool()->run( + [server, uri, handler]() { server->documentFoldingRange( uri, handler ); } ); + } else { + server->documentFoldingRange( uri, handler ); + } + + return { uri.toString() }; +} + void processFormattingResponse( const std::shared_ptr& doc, std::vector edits ) { TextRanges ranges = doc->getSelections(); @@ -689,6 +717,8 @@ PluginRequestHandle LSPClientPlugin::processMessage( const PluginMessage& msg ) return server->getCapabilities().workspaceSymbolProvider; case PluginCapability::TextDocumentSymbol: return server->getCapabilities().documentSymbolProvider; + case PluginCapability::FoldingRange: + return server->getCapabilities().foldingRangeProvider; default: { } } @@ -734,6 +764,10 @@ PluginRequestHandle LSPClientPlugin::processMessage( const PluginMessage& msg ) processDiagnosticsCodeAction( msg ); break; } + case PluginMessageType::FoldingRanges: { + processFoldingRanges( msg ); + break; + } default: break; } diff --git a/src/tools/ecode/plugins/lsp/lspclientplugin.hpp b/src/tools/ecode/plugins/lsp/lspclientplugin.hpp index 5bc89da62..2a8beb794 100644 --- a/src/tools/ecode/plugins/lsp/lspclientplugin.hpp +++ b/src/tools/ecode/plugins/lsp/lspclientplugin.hpp @@ -25,7 +25,7 @@ class LSPClientPlugin : public Plugin { public: static PluginDefinition Definition() { return { "lspclient", "LSP Client", "Language Server Protocol Client.", - LSPClientPlugin::New, { 0, 2, 4 }, LSPClientPlugin::NewSync }; + LSPClientPlugin::New, { 0, 2, 5 }, LSPClientPlugin::NewSync }; } static Plugin* New( PluginManager* pluginManager ); @@ -179,6 +179,8 @@ class LSPClientPlugin : public Plugin { PluginRequestHandle processTextDocumentSymbol( const PluginMessage& msg ); + PluginRequestHandle processFoldingRanges( const PluginMessage& msg ); + void setDocumentSymbols( const URI& docURI, LSPSymbolInformationList&& res ); void setDocumentSymbolsFromResponse( const PluginIDType& id, const URI& docURI, diff --git a/src/tools/ecode/plugins/lsp/lspclientserver.cpp b/src/tools/ecode/plugins/lsp/lspclientserver.cpp index 535595d62..5f51c7817 100644 --- a/src/tools/ecode/plugins/lsp/lspclientserver.cpp +++ b/src/tools/ecode/plugins/lsp/lspclientserver.cpp @@ -365,6 +365,7 @@ static void fromJson( LSPServerCapabilities& caps, const json& json ) { caps.documentHighlightProvider = toBoolOrObject( json, "documentHighlightProvider" ); caps.documentFormattingProvider = toBoolOrObject( json, "documentFormattingProvider" ); caps.workspaceSymbolProvider = toBoolOrObject( json, "workspaceSymbolProvider" ); + caps.foldingRangeProvider = toBoolOrObject( json, "foldingRangeProvider" ); if ( json.contains( "documentRangeFormattingProvider" ) ) caps.documentRangeFormattingProvider = toBoolOrObject( json, "documentRangeFormattingProvider" ); @@ -988,6 +989,31 @@ static LSPSemanticTokensDelta parseSemanticTokensDelta( const json& result ) { return ret; } +static std::vector parseFoldingRange( const json& result ) { + std::vector ranges; + if ( !result.is_array() ) + return ranges; + for ( const auto& range : result ) { + if ( !range.contains( "startLine" ) || !range.contains( "endLine" ) ) + continue; + LSPFoldingRange nrange; + nrange.startLine = range.value( "startLine", 0u ); + nrange.endLine = range.value( "endLine", 0u ); + auto kind = range.value( "kind", "region" ); + switch ( String::hash( kind ) ) { + case static_cast( LSPFoldingRangeKind::Comment ): + nrange.kind = LSPFoldingRangeKind::Comment; + case static_cast( LSPFoldingRangeKind::Imports ): + nrange.kind = LSPFoldingRangeKind::Imports; + case static_cast( LSPFoldingRangeKind::Region ): + default: + nrange.kind = LSPFoldingRangeKind::Region; + } + ranges.emplace_back( nrange ); + } + return ranges; +} + void LSPClientServer::registerCapabilities( const json& jcap ) { if ( !jcap.is_object() || !jcap.contains( "registrations" ) || !jcap["registrations"].is_array() ) @@ -1573,6 +1599,29 @@ LSPClientServer::LSPRequestHandle LSPClientServer::documentSymbols( const URI& d return send( newRequest( "textDocument/documentSymbol", params ), h, eh ); } +LSPClientServer::LSPRequestHandle +LSPClientServer::documentFoldingRange( const URI& document, const JsonReplyHandler& h, + const JsonReplyHandler& eh ) { + auto params = textDocumentParams( document ); + return send( newRequest( "textDocument/foldingRange", params ), h, eh ); +} + +LSPClientServer::LSPRequestHandle +LSPClientServer::documentFoldingRange( const URI& document, + const ReplyHandler>& h, + const ReplyHandler& eh ) { + return documentFoldingRange( + document, + [h]( const IdType& id, const json& json ) { + if ( h ) + h( id, parseFoldingRange( json ) ); + }, + [eh]( const IdType& id, const json& json ) { + if ( eh ) + eh( id, parseResponseError( json ) ); + } ); +} + LSPClientServer::LSPRequestHandle LSPClientServer::documentSymbols( const URI& document, const WReplyHandler& h, diff --git a/src/tools/ecode/plugins/lsp/lspclientserver.hpp b/src/tools/ecode/plugins/lsp/lspclientserver.hpp index 09a7e968e..b88086bbf 100644 --- a/src/tools/ecode/plugins/lsp/lspclientserver.hpp +++ b/src/tools/ecode/plugins/lsp/lspclientserver.hpp @@ -95,6 +95,14 @@ class LSPClientServer { const WReplyHandler& h, const ReplyHandler& eh = {} ); + LSPClientServer::LSPRequestHandle documentFoldingRange( const URI& document, + const JsonReplyHandler& h, + const JsonReplyHandler& eh ); + + LSPRequestHandle documentFoldingRange( const URI& document, + const ReplyHandler>& h, + const ReplyHandler& eh = {} ); + LSPRequestHandle documentSymbolsBroadcast( const URI& document ); LSPRequestHandle didOpen( const URI& document, const std::string& text, int version ); diff --git a/src/tools/ecode/plugins/lsp/lspdocumentclient.cpp b/src/tools/ecode/plugins/lsp/lspdocumentclient.cpp index 4aef4c323..ea28aa23d 100644 --- a/src/tools/ecode/plugins/lsp/lspdocumentclient.cpp +++ b/src/tools/ecode/plugins/lsp/lspdocumentclient.cpp @@ -18,6 +18,13 @@ LSPDocumentClient::LSPDocumentClient( LSPClientServer* server, TextDocument* doc notifyOpen(); requestSymbolsDelayed(); requestSemanticHighlightingDelayed(); + requestFoldRangeDelayed(); + doc->getFoldRangeService().setProvider( [this]( auto ) -> bool { + bool ret = mServer->getCapabilities().foldingRangeProvider; + if ( ret ) + requestFoldRangeDelayed(); + return ret; + } ); } LSPDocumentClient::~LSPDocumentClient() { @@ -27,6 +34,8 @@ LSPDocumentClient::~LSPDocumentClient() { sceneNode->removeActionsByTag( mTag ); if ( nullptr != sceneNode && 0 != mTagSemanticTokens ) sceneNode->removeActionsByTag( mTagSemanticTokens ); + if ( nullptr != sceneNode && 0 != mTagFoldRange ) + sceneNode->removeActionsByTag( mTagFoldRange ); mShutdown = true; while ( mRunningSemanticTokens ) Sys::sleep( Milliseconds( 0.1f ) ); @@ -35,6 +44,7 @@ LSPDocumentClient::~LSPDocumentClient() { void LSPDocumentClient::onDocumentLoaded( TextDocument* ) { refreshTag(); requestSemanticHighlightingDelayed(); + requestFoldRangeDelayed(); // requestCodeLens(); } @@ -112,6 +122,7 @@ int LSPDocumentClient::getVersion() const { void LSPDocumentClient::onServerInitialized() { requestSymbols(); requestSemanticHighlighting(); + requestFoldRange(); // requestCodeLens(); } @@ -119,6 +130,7 @@ void LSPDocumentClient::refreshTag() { String::HashType oldTag = mTag; mTag = String::hash( mDoc->getURI().toString() ); mTagSemanticTokens = String::hash( mDoc->getURI().toString() + ":semantictokens" ); + mTagFoldRange = String::hash( mDoc->getURI().toString() + ":foldrange" ); UISceneNode* sceneNode = getUISceneNode(); if ( nullptr != sceneNode && 0 != oldTag ) sceneNode->removeActionsByTag( oldTag ); @@ -382,6 +394,46 @@ void LSPDocumentClient::requestSymbols() { } } +void LSPDocumentClient::requestFoldRange() { + eeASSERT( mDoc ); + LSPClientServer* server = mServer; + if ( !server->getCapabilities().foldingRangeProvider ) + return; + URI uri = mDoc->getURI(); + auto handler = [uri, this]( const PluginIDType&, const std::vector& res ) { + std::vector regions; + regions.reserve( res.size() ); + for ( const auto& region : res ) + regions.push_back( { { region.startLine, 0 }, { region.endLine, 0 } } ); + mDoc->getFoldRangeService().setFoldingRegions( regions ); + }; + + if ( Engine::instance()->isMainThread() ) { + server->getThreadPool()->run( + [server, uri, handler]() { server->documentFoldingRange( uri, handler ); } ); + } else { + server->documentFoldingRange( uri, handler ); + } +} + +void LSPDocumentClient::requestFoldRangeDelayed() { + if ( !mServer || !mServer->getCapabilities().foldingRangeProvider ) + return; + UISceneNode* sceneNode = getUISceneNode(); + if ( sceneNode ) { + sceneNode->removeActionsByTag( mTagFoldRange ); + LSPDocumentClient* docClient = this; + URI uri = mDoc->getURI(); + LSPClientServer* server = mServer; + sceneNode->runOnMainThread( + [docClient, server, uri]() { + if ( server->hasDocument( uri ) ) + docClient->requestFoldRange(); + }, + Seconds( 1.f ), mTagFoldRange ); + } +} + void LSPDocumentClient::requestSymbolsDelayed() { if ( !mServer || !mServer->getCapabilities().documentSymbolProvider ) return; diff --git a/src/tools/ecode/plugins/lsp/lspdocumentclient.hpp b/src/tools/ecode/plugins/lsp/lspdocumentclient.hpp index 72f35e5a9..071b3bb97 100644 --- a/src/tools/ecode/plugins/lsp/lspdocumentclient.hpp +++ b/src/tools/ecode/plugins/lsp/lspdocumentclient.hpp @@ -58,6 +58,10 @@ class LSPDocumentClient : public TextDocument::Client { void requestSemanticHighlightingDelayed( bool reqFull = false ); + void requestFoldRange(); + + void requestFoldRangeDelayed(); + bool isRunningSemanticTokens() const; bool isWaitingSemanticTokensResponse() const; @@ -70,6 +74,7 @@ class LSPDocumentClient : public TextDocument::Client { TextDocument* mDoc{ nullptr }; String::HashType mTag{ 0 }; String::HashType mTagSemanticTokens{ 0 }; + String::HashType mTagFoldRange{ 0 }; int mVersion{ 0 }; std::string mSemanticeResultId; LSPSemanticTokensDelta mSemanticTokens; diff --git a/src/tools/ecode/plugins/lsp/lspprotocol.hpp b/src/tools/ecode/plugins/lsp/lspprotocol.hpp index e5059b574..30a7de1a1 100644 --- a/src/tools/ecode/plugins/lsp/lspprotocol.hpp +++ b/src/tools/ecode/plugins/lsp/lspprotocol.hpp @@ -3,6 +3,7 @@ #include #include +#include #include using namespace EE; @@ -151,6 +152,7 @@ struct LSPServerCapabilities { bool documentHighlightProvider = false; bool documentFormattingProvider = false; bool documentRangeFormattingProvider = false; + bool foldingRangeProvider = false; LSPCodeLensOptions codeLensProvider; bool workspaceSymbolProvider = false; LSPDocumentOnTypeFormattingOptions documentOnTypeFormattingProvider; @@ -602,6 +604,18 @@ struct LSPSemanticTokensDelta { std::vector data; }; +enum class LSPFoldingRangeKind : String::HashType { + Comment = String::hash( "comment" ), + Imports = String::hash( "imports" ), + Region = String::hash( "region" ), +}; + +struct LSPFoldingRange { + unsigned int startLine; + unsigned int endLine; + LSPFoldingRangeKind kind{ LSPFoldingRangeKind::Region }; +}; + } // namespace ecode #endif // ECODE_LSPCLIENTPROTOCOL_HPP diff --git a/src/tools/ecode/plugins/pluginmanager.hpp b/src/tools/ecode/plugins/pluginmanager.hpp index 9bbac722d..146ae33c0 100644 --- a/src/tools/ecode/plugins/pluginmanager.hpp +++ b/src/tools/ecode/plugins/pluginmanager.hpp @@ -64,7 +64,7 @@ struct PluginDefinition { PluginCreatorFn creatorSyncFn{ nullptr }; }; -enum class PluginCapability { WorkspaceSymbol, TextDocumentSymbol, Max }; +enum class PluginCapability { WorkspaceSymbol, TextDocumentSymbol, FoldingRange, Max }; enum class PluginMessageType { WorkspaceFolderChanged, // Broadcast the workspace folder from the application to the plugins @@ -92,6 +92,7 @@ enum class PluginMessageType { QueryPluginCapability, // Requests / queries if a plugin providers a capability UIReady, // Informs the Plugins that the UI is ready to be used UIThemeReloaded, // Informs the plugins that the UI theme has been reloaded + FoldingRanges, // Request to the LSP server the folding ranges of a document Undefined }; @@ -106,7 +107,8 @@ enum class PluginMessageFormat { ShowMessage, ShowDocument, SymbolInformation, - DiagnosticsCodeAction + DiagnosticsCodeAction, + FoldingRanges }; class PluginIDType {