From d3bc6f29350cbfed5c656a87062fdb3d181e5f12 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mart=C3=ADn=20Lucas=20Golini?= Date: Sun, 2 Feb 2025 19:07:41 -0300 Subject: [PATCH] Fix for multi-line signature help labels, now labels will be flatten into a single line (SpartanJ/ecode#388). Fix next signature help position. Fix signature help and suggestions styles for light color schemes, and some minor improvements for other schemes. Allow to set extra trigger characters for signature help in LSP configurations (fix zig zls not updating signature position after a "," input). Plus some other minor fixes. --- bin/assets/colorschemes/colorschemes.conf | 19 +++- bin/assets/plugins/debugger.json | 14 +-- bin/assets/plugins/lspclient.json | 3 +- bin/assets/ui/breeze.css | 8 +- include/eepp/core/string.hpp | 3 + include/eepp/ui/doc/textrange.hpp | 10 ++ src/eepp/core/string.cpp | 62 +++++++++--- src/eepp/ui/doc/syntaxtokenizer.cpp | 25 ++--- src/eepp/ui/doc/textrange.cpp | 62 ++++++++++++ .../autocomplete/autocompleteplugin.cpp | 95 ++++++++++++++++--- .../autocomplete/autocompleteplugin.hpp | 17 +++- .../debugger/debuggerclientlistener.cpp | 4 - .../ecode/plugins/debugger/debuggerplugin.cpp | 10 +- .../plugins/debugger/models/threadsmodel.cpp | 7 +- .../ecode/plugins/lsp/lspclientplugin.cpp | 16 +++- .../ecode/plugins/lsp/lspclientserver.cpp | 6 +- src/tools/ecode/plugins/lsp/lspdefinition.hpp | 1 + src/tools/ecode/plugins/lsp/lspprotocol.hpp | 2 +- 18 files changed, 292 insertions(+), 72 deletions(-) diff --git a/bin/assets/colorschemes/colorschemes.conf b/bin/assets/colorschemes/colorschemes.conf index 30db30db9..90a9b48d6 100644 --- a/bin/assets/colorschemes/colorschemes.conf +++ b/bin/assets/colorschemes/colorschemes.conf @@ -186,6 +186,8 @@ selection = #b7dce8 line_number = #d0d0d0 line_number2 = #808080 line_highlight = #f2f2f2 +suggestion = #404040,#e8e8e8 +suggestion_selected = #404040,#f7f7f7 normal = #181818 symbol = #181818 @@ -391,6 +393,9 @@ minimap_highlight = #FF00FFFF error = #990000FF; warning = #999900FF; notice = #8abdff +suggestion = #1d1f27,#e1e1e6 +suggestion_selected = #222533,#ffffff +suggestion_scrollbar = #3daee9 [solarized light] background = #fdf6e3 @@ -401,6 +406,9 @@ selection = #eee8d5 line_number = #93a1a1 line_number2 = #002b36 line_highlight = #eee8d5 +suggestion = #657b83,#f7f1de +suggestion_selected = #657b83,#eee8d5 +suggestion_scrollbar = #002b36 normal = #657b83 symbol = #657b83 @@ -583,6 +591,9 @@ minimap_highlight = #FF00FFFF error = #990000FF; warning = #999900FF; notice = #8abdff +suggestion = #1d1f27,#e1e1e6 +suggestion_selected = #222533,#f7f9f9 +suggestion_scrollbar = #ff5971 [solarobj] background = #fdf6e3 @@ -613,6 +624,10 @@ error = #990000FF; warning = #999900FF; notice = #8abdff +suggestion = #3e3c37,#ebe4d2 +suggestion_selected = #3e3c37,#fff8e4 +suggestion_scrollbar = #6c71c4 + [catppuccin macchiato] background = #24273a text = #cad3f5 @@ -669,8 +684,8 @@ line_break_column = #54575b99 matching_bracket = #dbdbdb matching_selection = #ffff00 matching_search = #0000f0 -suggestion = #e1e1e6,#1d1f27 -suggestion_selected = #ffffff,#222533 +suggestion = #1d1f27,#e1e1e6 +suggestion_selected = #222533,#ffffff suggestion_scrollbar = #3daee9 error = #ff3030 warning = yellow diff --git a/bin/assets/plugins/debugger.json b/bin/assets/plugins/debugger.json index fff95bdc2..2a4a84139 100644 --- a/bin/assets/plugins/debugger.json +++ b/bin/assets/plugins/debugger.json @@ -20,17 +20,6 @@ "env": "${env}" } }, - { - "name": "Launch binary in Terminal", - "request": "launch", - "arguments": { - "program": "${file}", - "args": "${args}", - "cwd": "${cwd}", - "env": "${env}", - "runInTerminal": true - } - }, { "name": "Attach to Name", "request": "attach", @@ -258,8 +247,7 @@ "request": "launch", "arguments": { "program": "${file}", - "args": "${args}", - "stopOnEntry": true + "args": "${args}" } } ] diff --git a/bin/assets/plugins/lspclient.json b/bin/assets/plugins/lspclient.json index 5a96f9dca..7864e827d 100644 --- a/bin/assets/plugins/lspclient.json +++ b/bin/assets/plugins/lspclient.json @@ -103,7 +103,8 @@ "name": "zls", "url": "https://github.com/zigtools/zls", "command": "zls", - "file_patterns": ["%.zig$"] + "file_patterns": ["%.zig$"], + "extra_trigger_chars": [ "," ] }, { "language": "odin", diff --git a/bin/assets/ui/breeze.css b/bin/assets/ui/breeze.css index 54720c8ac..462b82b34 100644 --- a/bin/assets/ui/breeze.css +++ b/bin/assets/ui/breeze.css @@ -302,7 +302,9 @@ ListView::cell { } ListBox:hover, -ListView:hover { +ListView:hover, +ListBox:focus, +ListView:focus { border-color: var(--primary); } @@ -383,8 +385,8 @@ SpinBox::input { } SpinBox::input, -SpinBox::input::hover, -SpinBox::input::focus { +SpinBox::input:hover, +SpinBox::input:focus { border-width: 0dp; border-color: transparent; } diff --git a/include/eepp/core/string.hpp b/include/eepp/core/string.hpp index b38317de7..feb1e36e7 100644 --- a/include/eepp/core/string.hpp +++ b/include/eepp/core/string.hpp @@ -437,6 +437,9 @@ class EE_API String { /** @return The number of codepoints of the utf8 string. */ static size_t utf8Length( const std::string_view& utf8String ); + /** Converts a character position from an utf8 string to a code point position */ + static size_t utf8ToCodepointPosition( const std::string_view& utf8Text, size_t utf8Pos ); + /** @return The next character in a utf8 null terminated string */ static Uint32 utf8Next( char*& utf8String ); diff --git a/include/eepp/ui/doc/textrange.hpp b/include/eepp/ui/doc/textrange.hpp index c302f0873..4a03fbccd 100644 --- a/include/eepp/ui/doc/textrange.hpp +++ b/include/eepp/ui/doc/textrange.hpp @@ -114,6 +114,12 @@ class EE_API TextRange { bool isNormalized() const { return mStart <= mEnd; } + static TextRange convertToLineColumn( const String::View& text, Int64 startOffset, + Int64 endOffset ); + + static TextRange convertToLineColumn( const std::string_view& text, Int64 startOffset, + Int64 endOffset ); + private: TextPosition mStart; TextPosition mEnd; @@ -121,6 +127,10 @@ class EE_API TextRange { TextPosition normalizedStart() const { return mStart < mEnd ? mStart : mEnd; } TextPosition normalizedEnd() const { return mStart < mEnd ? mEnd : mStart; } + + template + static TextRange convertToLineColumn( const StringType& text, Int64 startOffset, + Int64 endOffset ); }; class EE_API TextRanges : public std::vector { diff --git a/src/eepp/core/string.cpp b/src/eepp/core/string.cpp index 9a30db70c..dff88fee1 100644 --- a/src/eepp/core/string.cpp +++ b/src/eepp/core/string.cpp @@ -1261,18 +1261,18 @@ bool String::isAscii() const { size_t len = mString.size(); size_t i = 0; -// #ifdef EE_STD_SIMD -// using simd_type = simd::native_simd; -// constexpr size_t simd_size = simd_type::size(); -// const simd_type ascii_limit = 127; -// for ( ; i + simd_size - 1 < len; i += simd_size ) { -// simd_type chunk; -// chunk.copy_from( &data[i], simd::element_aligned ); -// auto mask = chunk > ascii_limit; -// if ( simd::any_of( mask ) ) -// return false; -// } -// #endif + // #ifdef EE_STD_SIMD + // using simd_type = simd::native_simd; + // constexpr size_t simd_size = simd_type::size(); + // const simd_type ascii_limit = 127; + // for ( ; i + simd_size - 1 < len; i += simd_size ) { + // simd_type chunk; + // chunk.copy_from( &data[i], simd::element_aligned ); + // auto mask = chunk > ascii_limit; + // if ( simd::any_of( mask ) ) + // return false; + // } + // #endif for ( ; i < len; ++i ) if ( data[i] > 127 ) @@ -1590,6 +1590,44 @@ Uint32 String::utf8Next( char*& utf8String ) { return utf8::unchecked::next( utf8String ); } +size_t String::utf8ToCodepointPosition( const std::string_view& utf8Text, size_t utf8Pos ) { + size_t codepointPos = 0; + size_t bytePos = 0; + + while ( bytePos < utf8Pos && bytePos < utf8Text.length() ) { + unsigned char c = utf8Text[bytePos]; + + // Skip continuation bytes (10xxxxxx) + if ( ( c & 0xC0 ) == 0x80 ) { + bytePos++; + continue; + } + + // Count this as one codepoint + codepointPos++; + + // Move to the next character + if ( c < 0x80 ) + bytePos += 1; // ASCII + else if ( c < 0xE0 ) + bytePos += 2; // 2-byte sequence + else if ( c < 0xF0 ) + bytePos += 3; // 3-byte sequence + else + bytePos += 4; // 4-byte sequence + + // Handle potential truncated sequences + if ( bytePos > utf8Text.length() ) + break; + } + + // If we're in the middle of a character, return the previous complete character position + if ( utf8Pos > bytePos ) + codepointPos--; + + return codepointPos; +} + String::operator std::string() const { return toUtf8(); } diff --git a/src/eepp/ui/doc/syntaxtokenizer.cpp b/src/eepp/ui/doc/syntaxtokenizer.cpp index 327e9e7ab..907807400 100644 --- a/src/eepp/ui/doc/syntaxtokenizer.cpp +++ b/src/eepp/ui/doc/syntaxtokenizer.cpp @@ -197,6 +197,7 @@ _tokenize( const SyntaxDefinition& syntax, const std::string& text, const Syntax PatternMatcher::Range matches[12]; int start, end; + const std::string_view textv{ text }; size_t numMatches; size_t i = startIndex; SyntaxState retState = state; @@ -228,7 +229,7 @@ _tokenize( const SyntaxDefinition& syntax, const std::string& text, const Syntax ( range.first == -1 || rangeSubsyntax.first < range.first ) ) { if ( !skipSubSyntaxSeparator ) { pushToken( tokens, curState.subsyntaxInfo->types[0], - text.substr( i, rangeSubsyntax.second - i ) ); + textv.substr( i, rangeSubsyntax.second - i ) ); } popSubsyntax( curState, retState, syntax ); i = rangeSubsyntax.second; @@ -239,16 +240,16 @@ _tokenize( const SyntaxDefinition& syntax, const std::string& text, const Syntax if ( !skip ) { if ( range.first != -1 ) { if ( range.second > range.first && pattern.types.size() >= 3 ) { - pushToken( tokens, pattern.types[0], text.substr( i, range.first - i ) ); + pushToken( tokens, pattern.types[0], textv.substr( i, range.first - i ) ); pushToken( tokens, pattern.types[pattern.types.size() - 1], - text.substr( range.first, range.second - range.first ) ); + textv.substr( range.first, range.second - range.first ) ); } else { - pushToken( tokens, pattern.types[0], text.substr( i, range.second - i ) ); + pushToken( tokens, pattern.types[0], textv.substr( i, range.second - i ) ); } setSubsyntaxPatternIdx( curState, retState, SYNTAX_TOKENIZER_STATE_NONE ); i = range.second; } else { - pushToken( tokens, pattern.types[0], text.substr( i ) ); + pushToken( tokens, pattern.types[0], textv.substr( i ) ); break; } } @@ -264,7 +265,7 @@ _tokenize( const SyntaxDefinition& syntax, const std::string& text, const Syntax if ( rangeSubsyntax.first != -1 ) { if ( !skipSubSyntaxSeparator ) { pushToken( tokens, curState.subsyntaxInfo->types[0], - text.substr( i, rangeSubsyntax.second - i ) ); + textv.substr( i, rangeSubsyntax.second - i ) ); } popSubsyntax( curState, retState, syntax ); i = rangeSubsyntax.second; @@ -292,7 +293,7 @@ _tokenize( const SyntaxDefinition& syntax, const std::string& text, const Syntax int patternMatchStart = matches[0].start; int patternMatchEnd = matches[0].end; std::string patternFullText( - text.substr( patternMatchStart, patternMatchEnd - patternMatchStart ) ); + textv.substr( patternMatchStart, patternMatchEnd - patternMatchStart ) ); auto patternType = pattern.types[0]; int lastStart = patternMatchStart; int lastEnd = patternMatchEnd; @@ -315,13 +316,13 @@ _tokenize( const SyntaxDefinition& syntax, const std::string& text, const Syntax if ( curMatch == 1 && start > lastStart ) { pushToken( tokens, patternType, - text.substr( patternMatchStart, start - patternMatchStart ) ); + textv.substr( patternMatchStart, start - patternMatchStart ) ); } else if ( start > lastEnd ) { pushToken( tokens, patternType, - text.substr( lastEnd, start - lastEnd ) ); + textv.substr( lastEnd, start - lastEnd ) ); } - patternText = text.substr( start, end - start ); + patternText = textv.substr( start, end - start ); SyntaxStyleType type = curState.currentSyntax->getSymbol( patternText ); if ( !skipSubSyntaxSeparator || !pattern.hasSyntax() ) { pushToken( tokens, @@ -344,7 +345,7 @@ _tokenize( const SyntaxDefinition& syntax, const std::string& text, const Syntax if ( curMatch == numMatches - 1 && end < patternMatchEnd ) { pushToken( tokens, patternType, - text.substr( end, patternMatchEnd - end ) ); + textv.substr( end, patternMatchEnd - end ) ); i = patternMatchEnd; } @@ -367,7 +368,7 @@ _tokenize( const SyntaxDefinition& syntax, const std::string& text, const Syntax String::utf8Next( strEnd ); end = start + ( strEnd - strStart ); } - patternText = text.substr( start, end - start ); + patternText = textv.substr( start, end - start ); SyntaxStyleType type = curState.currentSyntax->getSymbol( patternText ); if ( !skipSubSyntaxSeparator || !pattern.hasSyntax() ) { pushToken( tokens, diff --git a/src/eepp/ui/doc/textrange.cpp b/src/eepp/ui/doc/textrange.cpp index 1506bef67..71565c199 100644 --- a/src/eepp/ui/doc/textrange.cpp +++ b/src/eepp/ui/doc/textrange.cpp @@ -101,6 +101,68 @@ TextRange TextRange::fromString( const std::string& range ) { return {}; } +template +TextRange TextRange::convertToLineColumn( const StringType& text, Int64 startOffset, + Int64 endOffset ) { + if ( startOffset < 0 || endOffset < 0 || startOffset > static_cast( text.length() ) || + endOffset > static_cast( text.length() ) || startOffset > endOffset ) { + return {}; + } + + TextRange result; + Int64 currentLine = 0; + Int64 currentCol = 0; + Int64 currentPos = 0; + bool foundStart = false; + bool foundEnd = false; + size_t len = text.length(); + + for ( size_t i = 0; i < len; i++ ) { + if ( currentPos == startOffset ) { + result.setStart( { currentLine, currentCol } ); + foundStart = true; + } + + if ( currentPos == endOffset ) { + result.setEnd( { currentLine, currentCol } ); + foundEnd = true; + } + + if ( foundStart && foundEnd ) + break; + + if ( i == text.length() ) + break; + + if ( text[i] == '\n' ) { + currentLine++; + currentCol = 0; + } else { + currentCol++; + } + + currentPos++; + } + + if ( currentPos == startOffset ) + result.setStart( { currentLine, currentCol } ); + + if ( currentPos == endOffset ) + result.setEnd( { currentLine, currentCol } ); + + return result; +} + +TextRange TextRange::convertToLineColumn( const String::View& text, Int64 startOffset, + Int64 endOffset ) { + return convertToLineColumn( text, startOffset, endOffset ); +} + +TextRange TextRange::convertToLineColumn( const std::string_view& text, Int64 startOffset, + Int64 endOffset ) { + return convertToLineColumn( text, startOffset, endOffset ); +} + TextRanges::TextRanges() {} TextRanges::TextRanges( const std::vector& ranges ) : std::vector( ranges ) {} diff --git a/src/tools/ecode/plugins/autocomplete/autocompleteplugin.cpp b/src/tools/ecode/plugins/autocomplete/autocompleteplugin.cpp index 1dc5bc162..33e6c95d9 100644 --- a/src/tools/ecode/plugins/autocomplete/autocompleteplugin.cpp +++ b/src/tools/ecode/plugins/autocomplete/autocompleteplugin.cpp @@ -357,10 +357,9 @@ bool AutoCompletePlugin::onKeyDown( UICodeEditor* editor, const KeyEvent& event } } else if ( mShortcuts["autocomplete-next-signature-help"] == eventShortcut ) { if ( mSignatureHelp.signatures.size() > 1 ) { - mSignatureHelpSelected = - mSignatureHelpSelected == (int)mSignatureHelp.signatures.size() - 1 - ? mSignatureHelp.signatures.size() - 1 - : 0; + mSignatureHelpSelected = mSignatureHelpSelected <= 0 + ? mSignatureHelp.signatures.size() + : mSignatureHelpSelected; --mSignatureHelpSelected; mSignatureHelpSelected = mSignatureHelpSelected % mSignatureHelp.signatures.size(); editor->invalidateDraw(); @@ -818,9 +817,68 @@ AutoCompletePlugin::processSignatureHelp( const LSPSignatureHelp& signatureHelp } if ( !editor ) return {}; - editor->runOnMainThread( [this, editor, signatureHelp] { + + // Convert the LSP Signature Help into our own object: + // We will convert the UTF-8 label to UTF-32, then we will remove any new lines and extra spaces + // This guarantees that we always display a single line signature help + SignatureHelp signatures; + signatures.activeSignature = signatureHelp.activeSignature; + signatures.activeParameter = signatureHelp.activeParameter; + signatures.signatures.reserve( signatureHelp.signatures.size() ); + + TextDocument doc; + for ( const auto& sig : signatureHelp.signatures ) { + String initialLabel( sig.label ); + SignatureInformation nsig; + nsig.documentation = sig.documentation; + + doc.reset(); + doc.textInput( initialLabel ); + std::vector parameters; + parameters.reserve( sig.parameters.size() ); + + for ( size_t i = 0; i < sig.parameters.size(); i++ ) { + auto start = String::utf8ToCodepointPosition( sig.label, sig.parameters[i].start ); + auto end = String::utf8ToCodepointPosition( sig.label, sig.parameters[i].end ); + auto sel = TextRange::convertToLineColumn( initialLabel.view(), start, end ); + + if ( i == 0 ) + doc.setSelection( i, sel ); + else + doc.addSelection( sel ); + + parameters.emplace_back( doc.getSelectedText( i ) ); + } + + auto selections( doc.getSelections() ); + nsig.parameters.reserve( selections.size() ); + + if ( 0 != doc.replaceAll( "\n", "" ) ) { + while ( 0 != doc.replaceAll( " ", " " ) ) + ; + + nsig.label = doc.line( 0 ).getTextWithoutNewLine(); + + for ( const auto& param : parameters ) { + auto res = doc.find( param ); + if ( res.isValid() ) + nsig.parameters.emplace_back( res.result ); + } + } else { + nsig.label = std::move( initialLabel ); + + if ( !sig.parameters.empty() ) { + for ( auto& sel : selections ) + nsig.parameters.emplace_back( sel ); + } + } + + signatures.signatures.emplace_back( nsig ); + } + + editor->runOnMainThread( [this, editor, signatures = std::move( signatures )] { mSignatureHelpVisible = true; - mSignatureHelp = signatureHelp; + mSignatureHelp = signatures; if ( mSignatureHelp.signatures.empty() ) resetSignatureHelp(); editor->invalidateDraw(); @@ -942,7 +1000,7 @@ void AutoCompletePlugin::drawSignatureHelp( UICodeEditor* editor, const Vector2f primitives.setColor( Color( selectedStyle.background ).blendAlpha( editor->getAlpha() ) ); String str; if ( mSignatureHelp.signatures.size() > 1 ) { - str = String::format( "%s (%d of %zu)", curSig.label.c_str(), + str = String::format( "%s (%d of %zu)", curSig.label.toUtf8(), mSignatureHelpSelected == -1 ? 1 : mSignatureHelpSelected + 1, mSignatureHelp.signatures.size() ); } else { @@ -962,38 +1020,42 @@ void AutoCompletePlugin::drawSignatureHelp( UICodeEditor* editor, const Vector2f } bool hasParams = !curSig.parameters.empty(); - LSPParameterInformation curParam = + TextRange curParam = hasParams ? curSig.parameters[mSignatureHelp.activeParameter % curSig.parameters.size()] - : LSPParameterInformation{ -1, -1 }; + : TextRange{}; Rectf curParamRect; if ( hasParams ) { curParamRect = Rectf( { { boxRect.getPosition().x + mBoxPadding.Left + - curParam.start * editor->getGlyphWidth(), + curParam.start().column() * editor->getGlyphWidth(), boxRect.getPosition().y }, - { ( curParam.end - curParam.start ) * editor->getGlyphWidth(), mRowHeight } } ); + { ( curParam.end().column() - curParam.start().column() ) * editor->getGlyphWidth(), + mRowHeight } } ); if ( !editor->getScreenRect().contains( Rectf{ { curParamRect.getPosition().x + - ( curParam.end - curParam.start ) * editor->getGlyphWidth(), + ( curParam.end().column() - curParam.start().column() ) * + editor->getGlyphWidth(), curParamRect.getPosition().y }, curParamRect.getSize() } ) ) { auto offset = editor->getTextPositionOffset( mSignatureHelpPosition ); - pos = { static_cast( startScroll.x - curParam.start * editor->getGlyphWidth() + + pos = { static_cast( startScroll.x - + curParam.start().column() * editor->getGlyphWidth() + offset.x ), static_cast( startScroll.y + offset.y + vdiff ) }; boxRect.setPosition( pos ); curParamRect.setPosition( { boxRect.getPosition().x + mBoxPadding.Left + - curParam.start * editor->getGlyphWidth(), + curParam.start().column() * editor->getGlyphWidth(), boxRect.getPosition().y } ); } } primitives.drawRoundedRectangle( boxRect, 0.f, Vector2f::One, 6 ); - if ( hasParams && curParam.end - curParam.start > 0 && curParam.end < (int)str.size() ) { + if ( hasParams && curParam.end() != curParam.start() && + curParam.end().column() < (int)str.size() ) { primitives.setColor( matchingSelection.color ); primitives.drawRoundedRectangle( curParamRect, 0.f, Vector2f::One, 6 ); } @@ -1112,10 +1174,13 @@ void AutoCompletePlugin::postDraw( UICodeEditor* editor, const Vector2f& startSc LSPCompletionItemHelper::toIconString( suggestion.kind ), PixelDensity::dpToPxI( 12 ) ); if ( icon ) { + Color iconColor( icon->getColor() ); + icon->setColor( mSuggestionIndex == (int)i ? selectedStyle.color : normalStyle.color ); Vector2f padding( eefloor( ( iconSpace.getWidth() - icon->getSize().getWidth() ) * 0.5f ), eefloor( ( iconSpace.getHeight() - icon->getSize().getHeight() ) * 0.5f ) ); icon->draw( { cursorPos.x + padding.x, cursorPos.y + mRowHeight * count + padding.y } ); + icon->setColor( iconColor ); } if ( mSuggestionIndex == (int)i && !suggestion.documentation.value.empty() ) { diff --git a/src/tools/ecode/plugins/autocomplete/autocompleteplugin.hpp b/src/tools/ecode/plugins/autocomplete/autocompleteplugin.hpp index 6bbc86e4c..423b9048c 100644 --- a/src/tools/ecode/plugins/autocomplete/autocompleteplugin.hpp +++ b/src/tools/ecode/plugins/autocomplete/autocompleteplugin.hpp @@ -63,7 +63,7 @@ class AutoCompletePlugin : public Plugin { "Auto complete shows the completion popup as you type, so you can fill " "in long words by typing only a few characters.", AutoCompletePlugin::New, - { 0, 2, 6 }, + { 0, 2, 7 }, AutoCompletePlugin::NewSync }; } @@ -144,7 +144,20 @@ class AutoCompletePlugin : public Plugin { Int32 mSuggestionsStartIndex{ 0 }; std::unordered_map mCapabilities; Mutex mCapabilitiesMutex; - LSPSignatureHelp mSignatureHelp; + + struct SignatureInformation { + String label; + LSPMarkupContent documentation; + std::vector parameters; + }; + + struct SignatureHelp { + std::vector signatures; + int activeSignature{ 0 }; + int activeParameter{ 0 }; + }; + + SignatureHelp mSignatureHelp; TextPosition mSignatureHelpPosition; Int32 mSignatureHelpSelected{ -1 }; Mutex mHandlesMutex; diff --git a/src/tools/ecode/plugins/debugger/debuggerclientlistener.cpp b/src/tools/ecode/plugins/debugger/debuggerclientlistener.cpp index 6f03ee497..ff7b2376a 100644 --- a/src/tools/ecode/plugins/debugger/debuggerclientlistener.cpp +++ b/src/tools/ecode/plugins/debugger/debuggerclientlistener.cpp @@ -192,10 +192,6 @@ void DebuggerClientListener::sendBreakpoints() { Lock l( mPlugin->mBreakpointsMutex ); for ( const auto& fileBps : mPlugin->mBreakpoints ) { std::string path( fileBps.first ); - if ( isRemote() ) { - FileSystem::filePathRemoveBasePath( mPlugin->mProjectPath, path ); - path = "/" + path; - } mClient->setBreakpoints( path, fromSet( fileBps.second ) ); } } diff --git a/src/tools/ecode/plugins/debugger/debuggerplugin.cpp b/src/tools/ecode/plugins/debugger/debuggerplugin.cpp index 760b1e79c..f09de0fc3 100644 --- a/src/tools/ecode/plugins/debugger/debuggerplugin.cpp +++ b/src/tools/ecode/plugins/debugger/debuggerplugin.cpp @@ -330,6 +330,7 @@ static std::initializer_list DebuggerCommandList = { "debugger-breakpoint-enable-toggle", "debugger-start", "debugger-stop", + "debugger-start-stop", "debugger-step-over", "debugger-step-into", "debugger-step-out", @@ -448,7 +449,7 @@ void DebuggerPlugin::loadDAPConfig( const std::string& path, bool updateConfigFi } if ( mKeyBindings.empty() ) { - mKeyBindings["debugger-start"] = "mod+f5"; + mKeyBindings["debugger-start-stop"] = "mod+f5"; mKeyBindings["debugger-continue-interrupt"] = "f5"; mKeyBindings["debugger-breakpoint-toggle"] = "f9"; mKeyBindings["debugger-breakpoint-enable-toggle"] = "mod+f9"; @@ -1269,6 +1270,13 @@ void DebuggerPlugin::onRegisterDocument( TextDocument* doc ) { doc->setCommand( "debugger-stop", [this] { exitDebugger( true ); } ); + doc->setCommand( "debugger-start-stop", [this] { + if ( mDebugger ) + exitDebugger( true ); + else + runCurrentConfig(); + } ); + doc->setCommand( "debugger-breakpoint-toggle", [doc, this] { if ( setBreakpoint( doc, doc->getSelection().start().line() + 1 ) ) getUISceneNode()->getRoot()->invalidateDraw(); diff --git a/src/tools/ecode/plugins/debugger/models/threadsmodel.cpp b/src/tools/ecode/plugins/debugger/models/threadsmodel.cpp index 5c9776b3e..74a444c55 100644 --- a/src/tools/ecode/plugins/debugger/models/threadsmodel.cpp +++ b/src/tools/ecode/plugins/debugger/models/threadsmodel.cpp @@ -24,8 +24,11 @@ std::string ThreadsModel::columnName( const size_t& colIdx ) const { Variant ThreadsModel::data( const ModelIndex& modelIndex, ModelRole role ) const { if ( role == ModelRole::Display && modelIndex.column() == Columns::ID ) { - return Variant( String::format( "#%d (%s)", mThreads[modelIndex.row()].id, - mThreads[modelIndex.row()].name.c_str() ) ); + if ( mThreads[modelIndex.row()].name.empty() ) + return Variant( String::format( "#%d", mThreads[modelIndex.row()].id ) ); + else + return Variant( String::format( "#%d (%s)", mThreads[modelIndex.row()].id, + mThreads[modelIndex.row()].name.c_str() ) ); } else if ( role == ModelRole::Icon && modelIndex.column() == Columns::ID && mThreads[modelIndex.row()].id == mCurrentThreadId ) { static UIIcon* circleFilled = mSceneNode->findIcon( "circle-filled" ); diff --git a/src/tools/ecode/plugins/lsp/lspclientplugin.cpp b/src/tools/ecode/plugins/lsp/lspclientplugin.cpp index 0f55ee0bf..14648c505 100644 --- a/src/tools/ecode/plugins/lsp/lspclientplugin.cpp +++ b/src/tools/ecode/plugins/lsp/lspclientplugin.cpp @@ -1,5 +1,5 @@ -#include "lspclientplugin.hpp" #include "../../version.hpp" +#include "lspclientplugin.hpp" #include #include #include @@ -1284,6 +1284,7 @@ void LSPClientPlugin::loadLSPConfig( std::vector& lsps, const std lsp.host = tlsp.host; lsp.port = tlsp.port; lsp.initializationOptions = tlsp.initializationOptions; + lsp.extraTriggerChars = tlsp.extraTriggerChars; lsp.usesLSP = use; if ( obj.contains( "share_process" ) && obj["share_process"].is_boolean() ) { lsp.shareProcessWithOtherDefinition = obj["share_process"].get(); @@ -1320,13 +1321,22 @@ void LSPClientPlugin::loadLSPConfig( std::vector& lsps, const std auto& fp = obj["file_patterns"]; for ( auto& pattern : fp ) - lsp.filePatterns.push_back( pattern.get() ); + if ( pattern.is_string() ) + lsp.filePatterns.push_back( pattern.get() ); if ( obj.contains( "rootIndicationFileNames" ) ) { lsp.rootIndicationFileNames.clear(); auto& fnms = obj["rootIndicationFileNames"]; for ( auto& fn : fnms ) - lsp.rootIndicationFileNames.push_back( fn ); + if ( fn.is_string() ) + lsp.rootIndicationFileNames.push_back( fn ); + } + + if ( obj.contains( "extra_trigger_chars" ) && obj["extra_trigger_chars"].is_array() ) { + for ( auto& jch : obj["extra_trigger_chars"] ) { + if ( jch.is_string() ) + lsp.extraTriggerChars.push_back( jch ); + } } sanitizeCommand( lsp.command, mManager->getWorkspaceFolder() ); diff --git a/src/tools/ecode/plugins/lsp/lspclientserver.cpp b/src/tools/ecode/plugins/lsp/lspclientserver.cpp index b2727fa93..25fced745 100644 --- a/src/tools/ecode/plugins/lsp/lspclientserver.cpp +++ b/src/tools/ecode/plugins/lsp/lspclientserver.cpp @@ -1,5 +1,5 @@ -#include "lspclientserver.hpp" #include "lspclientplugin.hpp" +#include "lspclientserver.hpp" #include "lspclientservermanager.hpp" #include #include @@ -1201,6 +1201,10 @@ void LSPClientServer::initialize() { try { #endif fromJson( mCapabilities, resp["capabilities"] ); + + for ( const auto& ch : mLSP.extraTriggerChars ) + if ( !ch.empty() ) + mCapabilities.signatureHelpProvider.triggerCharacters.push_back( ch[0] ); #ifndef EE_DEBUG } catch ( const json::exception& e ) { Log::warning( diff --git a/src/tools/ecode/plugins/lsp/lspdefinition.hpp b/src/tools/ecode/plugins/lsp/lspdefinition.hpp index 8f31bb8fc..727c53a75 100644 --- a/src/tools/ecode/plugins/lsp/lspdefinition.hpp +++ b/src/tools/ecode/plugins/lsp/lspdefinition.hpp @@ -30,6 +30,7 @@ struct LSPDefinition { std::string usesLSP; bool shareProcessWithOtherDefinition{ false }; bool disabled{ false }; + std::vector extraTriggerChars; bool commandAvailable() const { auto cmdp( String::split( command, ' ' ) ); diff --git a/src/tools/ecode/plugins/lsp/lspprotocol.hpp b/src/tools/ecode/plugins/lsp/lspprotocol.hpp index 1ad011a3d..731693ce2 100644 --- a/src/tools/ecode/plugins/lsp/lspprotocol.hpp +++ b/src/tools/ecode/plugins/lsp/lspprotocol.hpp @@ -637,7 +637,7 @@ struct LSPPreviousResultId { using LSPPreviousResultIds = std::vector; static constexpr auto LSPDocumentDiagnosticReportKindFull = "full"; -static constexpr auto LSPDocumentDiagnosticReportKindUnchanged= "unchanged"; +static constexpr auto LSPDocumentDiagnosticReportKindUnchanged = "unchanged"; struct LSPFullDocumentDiagnosticReport { URI uri;