diff --git a/include/eepp/math/rect.hpp b/include/eepp/math/rect.hpp index 1898b9576..f001ff434 100644 --- a/include/eepp/math/rect.hpp +++ b/include/eepp/math/rect.hpp @@ -18,6 +18,8 @@ template class tRECT { tRECT copy() const; + void setPosition( const Vector2& pos ); + bool intersect( const tRECT& rect ) const; bool contains( const tRECT& rect ) const; @@ -255,6 +257,14 @@ template T tRECT::getHeight() const { return eeabs( Bottom - Top ); } +template void tRECT::setPosition( const Vector2& pos ) { + auto size = getSize(); + Left = pos.x; + Bottom = pos.y + size.y; + Right = pos.x + size.x; + Top = pos.y; +} + template void tRECT::expand( const tRECT& rect ) { Left = eemin( Left, rect.Left ); Bottom = eemax( Bottom, rect.Bottom ); diff --git a/include/eepp/scene/node.hpp b/include/eepp/scene/node.hpp index 406a4cd9f..ec84be7dc 100644 --- a/include/eepp/scene/node.hpp +++ b/include/eepp/scene/node.hpp @@ -424,6 +424,8 @@ class EE_API Node : public Transformable { const Vector2f& getScreenPos() const; + Rectf getScreenRect() const; + protected: typedef std::map> EventsMap; friend class EventDispatcher; diff --git a/projects/emscripten/make.sh b/projects/emscripten/make.sh index f704e1600..eab6091ad 100755 --- a/projects/emscripten/make.sh +++ b/projects/emscripten/make.sh @@ -3,6 +3,7 @@ # remember to first set the environment # source /path/to/emsdk/emsdk_env.sh cd $(dirname "$0") || exit +unset CPLUS_INCLUDE_PATH premake4 --file=../../premake4.lua --with-gles2 --with-static-eepp --platform=emscripten --with-backend=SDL2 gmake cd ../../make/emscripten/ || exit rm -rf ./assets diff --git a/projects/emscripten/make_mt.sh b/projects/emscripten/make_mt.sh index 7926fdb55..035561539 100755 --- a/projects/emscripten/make_mt.sh +++ b/projects/emscripten/make_mt.sh @@ -3,6 +3,7 @@ # remember to first set the environment # source /path/to/emsdk/emsdk_env.sh cd $(dirname "$0") || exit +unset CPLUS_INCLUDE_PATH premake4 --file=../../premake4.lua --with-emscripten-pthreads --with-gles2 --with-static-eepp --platform=emscripten --with-backend=SDL2 gmake cd ../../make/emscripten/ || exit rm -rf ./assets diff --git a/src/eepp/scene/node.cpp b/src/eepp/scene/node.cpp index 8684c590f..90bdf6042 100644 --- a/src/eepp/scene/node.cpp +++ b/src/eepp/scene/node.cpp @@ -550,6 +550,10 @@ const Vector2f& Node::getScreenPos() const { return mScreenPos; } +Rectf Node::getScreenRect() const { + return Rectf( getScreenPos(), getPixelsSize() ); +} + void Node::clipStart() { if ( mVisible && isClipped() ) { clipSmartEnable( mScreenPos.x, mScreenPos.y, mSize.getWidth(), mSize.getHeight() ); diff --git a/src/eepp/system/sys.cpp b/src/eepp/system/sys.cpp index 858b62286..c83cd17b2 100644 --- a/src/eepp/system/sys.cpp +++ b/src/eepp/system/sys.cpp @@ -597,8 +597,10 @@ double Sys::getSystemTime() { Uint64 Sys::getProcessID() { #if EE_PLATFORM == EE_PLATFORM_WIN return GetCurrentProcessId(); -#else +#elif EE_PLATFORM != EE_PLATFORM_EMSCRIPTEN return getpid(); +#else +#warning Sys::getProcessID() not implemented in this platform #endif } diff --git a/src/tools/ecode/plugins/autocomplete/autocompleteplugin.cpp b/src/tools/ecode/plugins/autocomplete/autocompleteplugin.cpp index 913af639d..dc825e1b1 100644 --- a/src/tools/ecode/plugins/autocomplete/autocompleteplugin.cpp +++ b/src/tools/ecode/plugins/autocomplete/autocompleteplugin.cpp @@ -539,6 +539,81 @@ void AutoCompletePlugin::update( UICodeEditor* ) { } } +void AutoCompletePlugin::drawSignatureHelp( UICodeEditor* editor, const Vector2f& startScroll, + const Float& lineHeight, bool drawUp ) { + + TextDocument& doc = editor->getDocument(); + Primitives primitives; + const SyntaxColorScheme& scheme = editor->getColorScheme(); + const auto& normalStyle = scheme.getEditorSyntaxStyle( "suggestion" ); + const auto& selectedStyle = scheme.getEditorSyntaxStyle( "suggestion_selected" ); + const auto& matchingSelection = scheme.getEditorSyntaxStyle( "matching_selection" ); + + auto curSigIdx = + mSignatureHelpSelected != -1 ? mSignatureHelpSelected : mSignatureHelp.activeSignature; + auto curSig = mSignatureHelp.signatures[curSigIdx]; + Float vdiff = drawUp ? -mRowHeight : mRowHeight; + Vector2f pos( startScroll.x + editor->getXOffsetCol( mSignatureHelpPosition ), + startScroll.y + mSignatureHelpPosition.line() * lineHeight + vdiff ); + 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(), + mSignatureHelpSelected == -1 ? 1 : mSignatureHelpSelected + 1, + mSignatureHelp.signatures.size() ); + } else { + str = curSig.label; + } + + Rectf boxRect( pos, Sizef( editor->getTextWidth( str ) + mBoxPadding.Left + mBoxPadding.Right, + mRowHeight ) ); + if ( boxRect.getPosition().x + boxRect.getSize().getWidth() > + editor->getScreenPos().x + editor->getPixelsSize().getWidth() ) { + boxRect.setPosition( + { eefloor( editor->getScreenPos().x + editor->getPixelsSize().getWidth() - + boxRect.getSize().getWidth() ), + boxRect.getPosition().y } ); + if ( boxRect.getPosition().x < editor->getScreenPos().x ) + boxRect.setPosition( { eefloor( editor->getScreenPos().x ), boxRect.getPosition().y } ); + } + auto curParam = curSig.parameters[mSignatureHelp.activeParameter % curSig.parameters.size()]; + auto curParamRect = Rectf( + { { boxRect.getPosition().x + mBoxPadding.Left + curParam.start * editor->getGlyphWidth(), + boxRect.getPosition().y }, + { ( curParam.end - curParam.start ) * editor->getGlyphWidth(), mRowHeight } } ); + + if ( !editor->getScreenRect().contains( + Rectf{ { curParamRect.getPosition().x + + ( curParam.end - curParam.start ) * editor->getGlyphWidth(), + curParamRect.getPosition().y }, + curParamRect.getSize() } ) ) { + pos = { startScroll.x - curParam.start * editor->getGlyphWidth() + + editor->getXOffsetCol( mSignatureHelpPosition ), + startScroll.y + mSignatureHelpPosition.line() * lineHeight + vdiff }; + + boxRect.setPosition( pos ); + + curParamRect.setPosition( + { boxRect.getPosition().x + mBoxPadding.Left + curParam.start * editor->getGlyphWidth(), + boxRect.getPosition().y } ); + } + + primitives.drawRoundedRectangle( boxRect, 0.f, Vector2f::One, 6 ); + + if ( curParam.end - curParam.start > 0 && curParam.end < (int)str.size() ) { + primitives.setColor( matchingSelection.color ); + primitives.drawRoundedRectangle( curParamRect, 0.f, Vector2f::One, 6 ); + } + + Text text( "", editor->getFont(), editor->getFontSize() ); + text.setFillColor( normalStyle.color ); + text.setStyle( normalStyle.style ); + text.setString( str ); + SyntaxTokenizer::tokenizeText( doc.getSyntaxDefinition(), editor->getColorScheme(), text ); + text.draw( boxRect.getPosition().x + mBoxPadding.Left, + boxRect.getPosition().y + mBoxPadding.Top ); +} + void AutoCompletePlugin::postDraw( UICodeEditor* editor, const Vector2f& startScroll, const Float& lineHeight, const TextPosition& cursor ) { bool drawsSuggestions = @@ -554,38 +629,13 @@ void AutoCompletePlugin::postDraw( UICodeEditor* editor, const Vector2f& startSc const SyntaxColorScheme& scheme = editor->getColorScheme(); const auto& normalStyle = scheme.getEditorSyntaxStyle( "suggestion" ); const auto& selectedStyle = scheme.getEditorSyntaxStyle( "suggestion_selected" ); + bool drawUp = true; - if ( drawsSignature ) { - auto cursig = - mSignatureHelp - .signatures[mSignatureHelpSelected != -1 ? mSignatureHelpSelected - : mSignatureHelp.activeSignature]; - Vector2f pos( startScroll.x + editor->getXOffsetCol( mSignatureHelpPosition ), - startScroll.y + mSignatureHelpPosition.line() * lineHeight - lineHeight - - mBoxPadding.Top - mBoxPadding.Bottom ); - 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(), - mSignatureHelpSelected == -1 ? 1 : mSignatureHelpSelected + 1, - mSignatureHelp.signatures.size() ); - } else { - str = cursig.label; - } - primitives.drawRoundedRectangle( - Rectf( pos, Sizef( editor->getTextWidth( str ) + mBoxPadding.Left + mBoxPadding.Right, - mRowHeight ) ), - 0.f, Vector2f::One, 6 ); - Text text( "", editor->getFont(), editor->getFontSize() ); - text.setFillColor( normalStyle.color ); - text.setStyle( normalStyle.style ); - text.setString( str ); - SyntaxTokenizer::tokenizeText( doc.getSyntaxDefinition(), editor->getColorScheme(), text ); - text.draw( pos.x + mBoxPadding.Left, pos.y + mBoxPadding.Top ); - } - - if ( !drawsSuggestions ) + if ( !drawsSuggestions ) { + if ( drawsSignature ) + drawSignatureHelp( editor, startScroll, lineHeight, drawUp ); return; + } SymbolsList suggestions; { @@ -599,8 +649,10 @@ void AutoCompletePlugin::postDraw( UICodeEditor* editor, const Vector2f& startSc size_t max = eemin( mSuggestionsMaxVisible, suggestions.size() ); mRowHeight = lineHeight + mBoxPadding.Top + mBoxPadding.Bottom; const auto& barStyle = scheme.getEditorSyntaxStyle( "suggestion_scrollbar" ); - if ( cursorPos.y + mRowHeight * max > editor->getPixelsSize().getHeight() ) + if ( cursorPos.y + mRowHeight * max > editor->getPixelsSize().getHeight() ) { cursorPos.y -= lineHeight + mRowHeight * max; + drawUp = false; + } size_t maxIndex = eemin( mSuggestionsStartIndex + mSuggestionsMaxVisible, suggestions.size() ); @@ -651,6 +703,9 @@ void AutoCompletePlugin::postDraw( UICodeEditor* editor, const Vector2f& startSc count++; } + if ( drawsSignature ) + drawSignatureHelp( editor, startScroll, lineHeight, drawUp ); + if ( max >= suggestions.size() ) return; @@ -787,8 +842,6 @@ void AutoCompletePlugin::resetSuggestions( UICodeEditor* editor ) { void AutoCompletePlugin::resetSignatureHelp() { mSignatureHelpVisible = false; - mSignatureHelpEditor = nullptr; - mSignatureHelpPosition = {}; mSignatureHelp.signatures.clear(); mSignatureHelp.activeSignature = 0; mSignatureHelp.activeParameter = 0; diff --git a/src/tools/ecode/plugins/autocomplete/autocompleteplugin.hpp b/src/tools/ecode/plugins/autocomplete/autocompleteplugin.hpp index d80ea8d9c..1c6b0e75c 100644 --- a/src/tools/ecode/plugins/autocomplete/autocompleteplugin.hpp +++ b/src/tools/ecode/plugins/autocomplete/autocompleteplugin.hpp @@ -179,6 +179,9 @@ class AutoCompletePlugin : public UICodeEditorPlugin { PluginRequestHandle processSignatureHelp( const LSPSignatureHelp& signatureHelp ); void resetSignatureHelp(); + + void drawSignatureHelp( UICodeEditor* editor, const Vector2f& startScroll, + const Float& lineHeight, bool drawUp ); }; } // namespace ecode diff --git a/src/tools/ecode/plugins/lsp/lspclientserver.cpp b/src/tools/ecode/plugins/lsp/lspclientserver.cpp index 9e72243a6..cdc79ff58 100644 --- a/src/tools/ecode/plugins/lsp/lspclientserver.cpp +++ b/src/tools/ecode/plugins/lsp/lspclientserver.cpp @@ -303,40 +303,6 @@ static void fromJson( LSPServerCapabilities& caps, const json& json ) { caps.ready = true; } -static std::vector parseDiagnosticsArr( const json& result ) { - std::vector ret; - for ( const auto& vdiag : result ) { - auto diag = vdiag; - auto range = parseRange( diag[MEMBER_RANGE] ); - auto severity = static_cast( diag["severity"].get() ); - auto code = diag.contains( "code" ) ? ( diag["code"].is_number_integer() - ? String::toString( diag["code"].get() ) - : diag.at( "code" ).get() ) - : ""; - auto source = diag.value( "source", "" ); - auto message = diag.value( MEMBER_MESSAGE, "" ); - std::vector relatedInfoList; - if ( diag.contains( "relatedInformation" ) ) { - const auto relatedInfo = diag.at( "relatedInformation" ); - for ( const auto& vrelated : relatedInfo ) { - auto related = vrelated; - auto relLocation = parseLocation( related.at( MEMBER_LOCATION ) ); - auto relMessage = related.at( MEMBER_MESSAGE ).get(); - relatedInfoList.push_back( { relLocation, relMessage } ); - } - } - ret.push_back( { range, severity, code, source, message, relatedInfoList } ); - } - return ret; -} - -static LSPPublishDiagnosticsParams parsePublishDiagnostics( const json& result ) { - LSPPublishDiagnosticsParams ret; - ret.uri = URI( result.at( MEMBER_URI ).get() ); - ret.diagnostics = parseDiagnosticsArr( result.at( MEMBER_DIAGNOSTICS ) ); - return ret; -} - static bool isPositionValid( const TextPosition& pos ) { return pos.column() >= 0 && pos.line() >= 0; } @@ -460,15 +426,19 @@ static LSPTextDocumentEdit parseTextDocumentEdit( const json& result ) { static LSPWorkspaceEdit parseWorkSpaceEdit( const json& result ) { LSPWorkspaceEdit ret; - auto changes = result.at( "changes" ); - for ( auto it = changes.begin(); it != changes.end(); ++it ) { - ret.changes.insert( std::pair>( - URI( it.key() ), parseTextEditArray( it.value() ) ) ); + if ( result.contains( "changes" ) ) { + auto changes = result.at( "changes" ); + for ( auto it = changes.begin(); it != changes.end(); ++it ) { + ret.changes.insert( std::pair>( + URI( it.key() ), parseTextEditArray( it.value() ) ) ); + } } - auto documentChanges = result.at( "documentChanges" ); - // resourceOperations not supported for now - for ( auto edit : documentChanges ) { - ret.documentChanges.push_back( parseTextDocumentEdit( edit ) ); + if ( result.contains( "documentChanges" ) ) { + auto documentChanges = result.at( "documentChanges" ); + // resourceOperations not supported for now + for ( auto edit : documentChanges ) { + ret.documentChanges.push_back( parseTextDocumentEdit( edit ) ); + } } return ret; } @@ -516,7 +486,7 @@ static std::vector parseCodeAction( const json& result ) { for ( const auto& vaction : codeActions ) { auto& action = vaction; // entry could be Command or CodeAction - if ( !action.at( MEMBER_COMMAND ).is_string() ) { + if ( !action.contains( MEMBER_COMMAND ) || !action.at( MEMBER_COMMAND ).is_string() ) { // CodeAction auto title = action.at( MEMBER_TITLE ).get(); auto kind = action.value( MEMBER_KIND, "" ); @@ -539,6 +509,24 @@ static std::vector parseCodeAction( const json& result ) { return ret; } +static std::vector parseDiagnosticsCodeAction( const json& result ) { + std::vector ret; + const auto& codeActions = result; + for ( const auto& action : codeActions ) { + if ( !action.contains( MEMBER_COMMAND ) || !action.at( MEMBER_COMMAND ).is_string() ) { + auto title = action.at( MEMBER_TITLE ).get(); + auto kind = action.value( MEMBER_KIND, "" ); + auto isPreferred = action.value( "isPreferred", false ); + auto edit = action.contains( MEMBER_EDIT ) + ? parseWorkSpaceEdit( action.at( MEMBER_EDIT ) ) + : LSPWorkspaceEdit{}; + LSPDiagnosticsCodeAction action = { title, kind, isPreferred, edit }; + ret.push_back( action ); + } + } + return ret; +} + static json toJson( const LSPLocation& location ) { if ( !location.uri.empty() ) { return json{ { MEMBER_URI, location.uri.toString() }, @@ -639,6 +627,42 @@ static std::vector supportedSemanticTokenTypes() { "string", "number", "regexp", "operator" }; } +static std::vector parseDiagnosticsArr( const json& result ) { + std::vector ret; + for ( const auto& diag : result ) { + auto range = parseRange( diag[MEMBER_RANGE] ); + auto severity = static_cast( diag["severity"].get() ); + auto code = diag.contains( "code" ) ? ( diag["code"].is_number_integer() + ? String::toString( diag["code"].get() ) + : diag.at( "code" ).get() ) + : ""; + auto source = diag.value( "source", "" ); + auto message = diag.value( MEMBER_MESSAGE, "" ); + std::vector relatedInfoList; + if ( diag.contains( "relatedInformation" ) ) { + const auto& relatedInfo = diag.at( "relatedInformation" ); + for ( const auto& related : relatedInfo ) { + auto relLocation = parseLocation( related.at( MEMBER_LOCATION ) ); + auto relMessage = related.at( MEMBER_MESSAGE ).get(); + relatedInfoList.push_back( { relLocation, relMessage } ); + } + } + // clang providers codeActions from diagnostics + std::vector codeActions; + if ( diag.contains( "codeActions" ) ) + codeActions = parseDiagnosticsCodeAction( diag["codeActions"] ); + ret.push_back( { range, severity, code, source, message, relatedInfoList, codeActions } ); + } + return ret; +} + +static LSPPublishDiagnosticsParams parsePublishDiagnostics( const json& result ) { + LSPPublishDiagnosticsParams ret; + ret.uri = URI( result.at( MEMBER_URI ).get() ); + ret.diagnostics = parseDiagnosticsArr( result.at( MEMBER_DIAGNOSTICS ) ); + return ret; +} + static LSPCompletionList parseDocumentCompletion( const json& result ) { LSPCompletionList ret; if ( result.empty() ) @@ -699,7 +723,7 @@ static LSPSignatureInformation parseSignatureInformation( const json& json ) { } else { auto sub = label.get(); if ( sub.length() ) { - begin = info.label.find_first_of( sub ); + begin = info.label.find( sub ); if ( begin >= 0 ) end = begin + sub.length(); } @@ -771,7 +795,8 @@ void LSPClientServer::initialize() { { "textDocument", json{ { "documentSymbol", json{ { "hierarchicalDocumentSymbolSupport", true } } }, - { "publishDiagnostics", json{ { "relatedInformation", true } } }, + { "publishDiagnostics", + json{ { "relatedInformation", true }, { "codeActionsInline", true } } }, { "codeAction", codeAction }, { "semanticTokens", semanticTokens }, { "synchronization", json{ { "didSave", true } } }, diff --git a/src/tools/ecode/plugins/lsp/lspprotocol.hpp b/src/tools/ecode/plugins/lsp/lspprotocol.hpp index dfb2b96eb..2cbbd3c4b 100644 --- a/src/tools/ecode/plugins/lsp/lspprotocol.hpp +++ b/src/tools/ecode/plugins/lsp/lspprotocol.hpp @@ -132,12 +132,37 @@ enum class LSPDiagnosticSeverity { Hint = 4, }; +using LSPTextEdit = LSPTextDocumentContentChangeEvent; + +struct LSPVersionedTextDocumentIdentifier { + URI uri; + int version = -1; +}; + +struct LSPTextDocumentEdit { + LSPVersionedTextDocumentIdentifier textDocument; + std::vector edits; +}; + +struct LSPWorkspaceEdit { + // supported part for now + std::map> changes; + std::vector documentChanges; +}; + struct LSPDiagnosticRelatedInformation { // empty url / invalid range when absent LSPLocation location; std::string message; }; +struct LSPDiagnosticsCodeAction { + std::string title; + std::string kind; + bool isPreferred{ false }; + LSPWorkspaceEdit edit; +}; + struct LSPDiagnostic { TextRange range; LSPDiagnosticSeverity severity; @@ -145,6 +170,7 @@ struct LSPDiagnostic { std::string source; std::string message; std::vector relatedInformation; + std::vector codeActions; }; struct LSPPublishDiagnosticsParams { @@ -159,24 +185,6 @@ struct LSPCommand { nlohmann::json arguments; }; -struct LSPVersionedTextDocumentIdentifier { - URI uri; - int version = -1; -}; - -using LSPTextEdit = LSPTextDocumentContentChangeEvent; - -struct LSPTextDocumentEdit { - LSPVersionedTextDocumentIdentifier textDocument; - std::vector edits; -}; - -struct LSPWorkspaceEdit { - // supported part for now - std::map> changes; - std::vector documentChanges; -}; - struct LSPCodeAction { std::string title; std::string kind;