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.
This commit is contained in:
Martín Lucas Golini
2025-02-02 19:07:41 -03:00
parent 863c0ec1c0
commit d3bc6f2935
18 changed files with 292 additions and 72 deletions

View File

@@ -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<String> 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<Float>( startScroll.x - curParam.start * editor->getGlyphWidth() +
pos = { static_cast<Float>( startScroll.x -
curParam.start().column() * editor->getGlyphWidth() +
offset.x ),
static_cast<Float>( 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() ) {

View File

@@ -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<std::string, LSPServerCapabilities> mCapabilities;
Mutex mCapabilitiesMutex;
LSPSignatureHelp mSignatureHelp;
struct SignatureInformation {
String label;
LSPMarkupContent documentation;
std::vector<TextRange> parameters;
};
struct SignatureHelp {
std::vector<SignatureInformation> signatures;
int activeSignature{ 0 };
int activeParameter{ 0 };
};
SignatureHelp mSignatureHelp;
TextPosition mSignatureHelpPosition;
Int32 mSignatureHelpSelected{ -1 };
Mutex mHandlesMutex;

View File

@@ -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 ) );
}
}

View File

@@ -330,6 +330,7 @@ static std::initializer_list<std::string> 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();

View File

@@ -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" );

View File

@@ -1,5 +1,5 @@
#include "lspclientplugin.hpp"
#include "../../version.hpp"
#include "lspclientplugin.hpp"
#include <eepp/graphics/primitives.hpp>
#include <eepp/system/filesystem.hpp>
#include <eepp/system/lock.hpp>
@@ -1284,6 +1284,7 @@ void LSPClientPlugin::loadLSPConfig( std::vector<LSPDefinition>& 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<bool>();
@@ -1320,13 +1321,22 @@ void LSPClientPlugin::loadLSPConfig( std::vector<LSPDefinition>& lsps, const std
auto& fp = obj["file_patterns"];
for ( auto& pattern : fp )
lsp.filePatterns.push_back( pattern.get<std::string>() );
if ( pattern.is_string() )
lsp.filePatterns.push_back( pattern.get<std::string>() );
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() );

View File

@@ -1,5 +1,5 @@
#include "lspclientserver.hpp"
#include "lspclientplugin.hpp"
#include "lspclientserver.hpp"
#include "lspclientservermanager.hpp"
#include <algorithm>
#include <eepp/system/filesystem.hpp>
@@ -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(

View File

@@ -30,6 +30,7 @@ struct LSPDefinition {
std::string usesLSP;
bool shareProcessWithOtherDefinition{ false };
bool disabled{ false };
std::vector<std::string> extraTriggerChars;
bool commandAvailable() const {
auto cmdp( String::split( command, ' ' ) );

View File

@@ -637,7 +637,7 @@ struct LSPPreviousResultId {
using LSPPreviousResultIds = std::vector<LSPPreviousResultId>;
static constexpr auto LSPDocumentDiagnosticReportKindFull = "full";
static constexpr auto LSPDocumentDiagnosticReportKindUnchanged= "unchanged";
static constexpr auto LSPDocumentDiagnosticReportKindUnchanged = "unchanged";
struct LSPFullDocumentDiagnosticReport {
URI uri;