eepp: Improved PopUpMenu positioning.

ecode: Added quick-fix support.
This commit is contained in:
Martín Lucas Golini
2023-03-12 21:19:14 -03:00
parent c3de302ccd
commit a78cdadfc3
8 changed files with 137 additions and 18 deletions
+18
View File
@@ -688,6 +688,7 @@ void UIMenu::findBestMenuPos( Vector2f& pos, UIMenu* menu, UIMenu* parent,
Rectf qScreen( 0.f, 0.f, sceneNode->getPixelsSize().getWidth(),
sceneNode->getPixelsSize().getHeight() );
Vector2f oriPos( pos );
Rectf qPos( pos.x, pos.y, pos.x + menu->getPixelsSize().getWidth(),
pos.y + menu->getPixelsSize().getHeight() );
@@ -771,6 +772,23 @@ void UIMenu::findBestMenuPos( Vector2f& pos, UIMenu* menu, UIMenu* parent,
pos.y += menu->getPixelsSize().getHeight();
qPos.Top += menu->getPixelsSize().getHeight();
qPos.Bottom += menu->getPixelsSize().getHeight();
if ( !qScreen.contains( qPos ) ) {
pos = oriPos;
pos.y -= menu->getPixelsSize().getHeight();
qPos.Left = pos.x;
qPos.Right = qPos.Left + menu->getPixelsSize().getWidth();
qPos.Top = pos.y;
qPos.Bottom = qPos.Top + menu->getPixelsSize().getHeight();
if ( !qScreen.contains( qPos ) ) {
pos.y = qScreen.Bottom - menu->getPixelsSize().getHeight();
qPos.Left = pos.x;
qPos.Right = qPos.Left + menu->getPixelsSize().getWidth();
qPos.Top = pos.y;
qPos.Bottom = qPos.Top + menu->getPixelsSize().getHeight();
}
}
}
}
}
+1 -1
View File
@@ -3279,7 +3279,7 @@ void App::init( const LogLevel& logLevel, std::string file, const Float& pidelDe
{ "symbol-boolean", 0xea8f }, { "symbol-array", 0xea8a },
{ "symbol-object", 0xea8b }, { "symbol-key", 0xea93 },
{ "symbol-null", 0xea8f }, { "collapse-all", 0xeac5 },
{ "chevron-right", 0xeab6 } };
{ "chevron-right", 0xeab6 }, { "lightbulb-autofix", 0xeb13 } };
for ( const auto& icon : codIcons )
iconTheme->add( UIGlyphIcon::New( icon.first, codIconFont, icon.second ) );
@@ -8,6 +8,7 @@
#include <eepp/system/lock.hpp>
#include <eepp/system/luapattern.hpp>
#include <eepp/system/process.hpp>
#include <eepp/ui/uiiconthememanager.hpp>
#include <eepp/ui/uitooltip.hpp>
#include <nlohmann/json.hpp>
#include <random>
@@ -309,6 +310,38 @@ void LinterPlugin::setMatches( TextDocument* doc, const MatchOrigin& origin,
}
PluginRequestHandle LinterPlugin::processMessage( const PluginMessage& notification ) {
if ( notification.type == PluginMessageType::DiagnosticsCodeAction &&
notification.format == PluginMessageFormat::JSON && notification.isRequest() ) {
PluginIDType id( std::numeric_limits<Int64>::max() );
TextDocument* doc = getDocumentFromURI( notification.asJSON().value( "uri", "" ) );
if ( doc ) {
Lock l( mMatchesMutex );
auto foundMatch = mMatches.find( doc );
if ( foundMatch != mMatches.end() ) {
auto pos = TextPosition::fromString( notification.asJSON().value( "pos", "" ) );
if ( pos.isValid() ) {
auto foundLine = foundMatch->second.find( pos.line() );
if ( foundLine != foundMatch->second.end() ) {
LSPDiagnosticsCodeAction quickFix;
for ( const auto& match : foundLine->second ) {
if ( !match.codeActions.empty() ) {
for ( const auto& ca : match.codeActions ) {
quickFix = ca;
if ( quickFix.isPreferred )
break;
}
}
}
mManager->sendResponse( this, PluginMessageType::DiagnosticsCodeAction,
PluginMessageFormat::DiagnosticsCodeAction,
&quickFix, id );
}
}
}
}
return {};
}
if ( !mEnableLSPDiagnostics || notification.type != PluginMessageType::Diagnostics ||
notification.format != PluginMessageFormat::Diagnostics )
return PluginRequestHandle::empty();
@@ -722,6 +755,7 @@ void LinterPlugin::drawAfterLineText( UICodeEditor* editor, const Int64& index,
TextDocument* doc = matchIt->first;
std::vector<LinterMatch>& matches = lineIt->second;
Float sepSpace = PixelDensity::dpToPx( 24.f );
bool quickFixRendered = false;
for ( size_t i = 0; i < matches.size(); ++i ) {
auto& match = matches[i];
@@ -763,10 +797,32 @@ void LinterPlugin::drawAfterLineText( UICodeEditor* editor, const Int64& index,
match.box[editor] = box;
line.draw( pos.x, pos.y + lineHeight * 0.5f );
Float rLineWidth = 0;
if ( !quickFixRendered && doc->getSelection().start().line() == index &&
!match.codeActions.empty() ) {
rLineWidth = editor->getLineWidth( index );
Color wcolor( editor->getColorScheme().getEditorSyntaxStyle( "warning" ).color );
UIIcon* notification =
editor->getUISceneNode()->getUIIconThemeManager()->findIcon( "lightbulb-autofix" );
Float size = lineHeight;
Drawable* drawable = notification->getSize( (int)eefloor( size ) );
if ( drawable == nullptr )
return;
Color oldColor( drawable->getColor() );
drawable->setColor( wcolor );
drawable->draw( { position.x + rLineWidth, position.y } );
drawable->setColor( oldColor );
quickFixRendered = true;
}
if ( !mErrorLens || i != 0 )
continue;
Float lineWidth = editor->getLineWidth( index ) + sepSpace;
if ( rLineWidth == 0 )
rLineWidth = editor->getLineWidth( index );
Float lineWidth = rLineWidth + sepSpace;
Float realSpace = editor->getViewportWidth();
Float spaceWidth = realSpace - lineWidth;
if ( spaceWidth < sepSpace )
@@ -796,14 +852,14 @@ void LinterPlugin::minimapDrawBeforeLineText( UICodeEditor* editor, const Int64&
if ( matchIt == mMatches.end() )
return;
std::map<Int64, std::vector<LinterMatch>>& map = matchIt->second;
const std::map<Int64, std::vector<LinterMatch>>& map = matchIt->second;
auto lineIt = map.find( index );
if ( lineIt == map.end() )
return;
TextDocument* doc = matchIt->first;
std::vector<LinterMatch>& matches = lineIt->second;
const std::vector<LinterMatch>& matches = lineIt->second;
Primitives p;
for ( auto& match : matches ) {
for ( const auto& match : matches ) {
if ( match.lineCache != doc->line( index ).getHash() )
return;
Color col(
@@ -504,7 +504,20 @@ void LSPClientPlugin::createLocationsView( UICodeEditor* editor,
void LSPClientPlugin::createCodeActionsView( UICodeEditor* editor,
const std::vector<LSPCodeAction>& cas ) {
auto model = LSPCodeActionModel::create( mManager->getSplitter()->getUISceneNode(), cas );
bool casEmpty = cas.empty();
std::shared_ptr<LSPCodeActionModel> model;
if ( mQuickFix.title.empty() && mQuickFix.kind.empty() &&
mQuickFix.edit.documentChanges.empty() && mQuickFix.edit.changes.empty() ) {
model = LSPCodeActionModel::create( mManager->getSplitter()->getUISceneNode(), cas );
} else {
casEmpty = false;
auto casN( cas );
LSPCodeAction action{ mQuickFix.title, mQuickFix.kind, {}, mQuickFix.edit, {},
mQuickFix.isPreferred };
casN.insert( casN.begin(), action );
model = LSPCodeActionModel::create( mManager->getSplitter()->getUISceneNode(), casN );
}
createListView(
editor, model,
[this, editor]( const ModelEvent* modelEvent ) {
@@ -514,18 +527,21 @@ void LSPClientPlugin::createCodeActionsView( UICodeEditor* editor,
const auto cam = static_cast<const LSPCodeActionModel*>( modelEvent->getModel() );
if ( cam->hasCodeActions() && editorExists( editor ) ) {
const auto& ca = cam->getCodeAction( r );
auto server = mClientManager.getOneLSPClientServer( editor->getDocumentRef() );
if ( server && server->getCapabilities().executeCommandProvider ) {
mClientManager.executeCommand( editor->getDocumentRef(), ca.command );
} else {
if ( !ca.edit.changes.empty() || !ca.edit.documentChanges.empty() )
mClientManager.applyWorkspaceEdit( ca.edit, []( const auto& ) {} );
}
auto server = mClientManager.getOneLSPClientServer( editor->getDocumentRef() );
if ( server && server->getCapabilities().executeCommandProvider )
mClientManager.executeCommand( editor->getDocumentRef(), ca.command );
editor->setFocus();
} else if ( editorExists( editor ) ) {
editor->setFocus();
}
modelEvent->getNode()->close();
},
[this, cas, editor]( UIListView* lv ) {
if ( cas.empty() ) {
[this, casEmpty, editor]( UIListView* lv ) {
if ( casEmpty ) {
lv->runOnMainThread(
[this, editor] {
if ( editorExists( editor ) &&
@@ -604,6 +620,10 @@ PluginRequestHandle LSPClientPlugin::processMessage( const PluginMessage& msg )
processTextDocumentSymbol( msg );
break;
}
case PluginMessageType::DiagnosticsCodeAction: {
processDiagnosticsCodeAction( msg );
break;
}
default:
break;
}
@@ -863,7 +883,21 @@ void LSPClientPlugin::getAndGoToLocation( UICodeEditor* editor, const std::strin
} );
}
void LSPClientPlugin::processDiagnosticsCodeAction( const PluginMessage& msg ) {
if ( !( msg.isResponse() && msg.type == PluginMessageType::DiagnosticsCodeAction &&
msg.format == PluginMessageFormat::DiagnosticsCodeAction ) )
return;
mQuickFix = msg.asDiasnosticsCodeAction();
}
void LSPClientPlugin::codeAction( UICodeEditor* editor ) {
json j;
j["uri"] = editor->getDocument().getURI().toString();
j["pos"] = editor->getDocument().getSelection().start().toString();
mQuickFix = {};
auto req = mManager->sendRequest( PluginMessageType::DiagnosticsCodeAction,
PluginMessageFormat::JSON, &j );
mClientManager.codeAction(
editor->getDocumentRef(),
[&, editor]( const LSPClientServer::IdType&, const std::vector<LSPCodeAction>& res ) {
@@ -111,6 +111,7 @@ class LSPClientPlugin : public UICodeEditorPlugin {
Time mHoverDelay{ Seconds( 1.f ) };
Uint32 mOldTextStyle{ 0 };
Uint32 mOldTextAlign{ 0 };
LSPDiagnosticsCodeAction mQuickFix;
LSPClientPlugin( PluginManager* pluginManager, bool sync );
@@ -168,6 +169,8 @@ class LSPClientPlugin : public UICodeEditorPlugin {
void setDocumentSymbolsFromResponse( const PluginIDType& id, const URI& docURI,
LSPSymbolInformationList&& res );
void processDiagnosticsCodeAction( const PluginMessage& msg );
};
} // namespace ecode
@@ -573,8 +573,9 @@ static std::vector<LSPCodeAction> parseCodeAction( const json& result ) {
auto diagnostics = action.contains( MEMBER_DIAGNOSTICS )
? parseDiagnostics( action.at( MEMBER_DIAGNOSTICS ) )
: std::vector<LSPDiagnostic>{};
LSPCodeAction action = { title, kind, diagnostics, edit, command };
ret.push_back( action );
auto isPreferred = action.value( "isPreferred", false );
LSPCodeAction _action = { title, kind, diagnostics, edit, command, isPreferred };
ret.push_back( _action );
} else {
// Command
auto command = parseCommand( action );
@@ -595,8 +596,8 @@ static std::vector<LSPDiagnosticsCodeAction> parseDiagnosticsCodeAction( const j
auto edit = action.contains( MEMBER_EDIT )
? parseWorkSpaceEdit( action.at( MEMBER_EDIT ) )
: LSPWorkspaceEdit{};
LSPDiagnosticsCodeAction action = { title, kind, isPreferred, edit };
ret.push_back( action );
LSPDiagnosticsCodeAction _action = { title, kind, isPreferred, edit };
ret.push_back( _action );
}
}
return ret;
@@ -201,6 +201,7 @@ struct LSPCodeAction {
std::vector<LSPDiagnostic> diagnostics;
LSPWorkspaceEdit edit;
LSPCommand command;
bool isPreferred{ false };
};
enum class LSPWorkDoneProgressKind { Begin, Report, End };
+7 -1
View File
@@ -76,6 +76,7 @@ enum class PluginMessageType {
WorkspaceSymbol, // Request to the LSP server to query workspace symbols
TextDocumentSymbol, // Request to the LSP server the document symbols
TextDocumentFlattenSymbol, // Request to the LSP server the document symbols flattened
DiagnosticsCodeAction, // Request a code action to anyone that can handle it
Undefined
};
@@ -88,7 +89,8 @@ enum class PluginMessageFormat {
ProjectSearchResult,
ShowMessage,
ShowDocument,
SymbolInformation
SymbolInformation,
DiagnosticsCodeAction
};
class PluginIDType {
@@ -180,6 +182,10 @@ struct PluginMessage {
return *static_cast<const LSPSymbolInformationList*>( data );
}
const LSPDiagnosticsCodeAction& asDiasnosticsCodeAction() const {
return *static_cast<const LSPDiagnosticsCodeAction*>( data );
}
const PluginIDType& asPluginID() const { return *static_cast<const PluginIDType*>( data ); }
bool isResponse() const { return -1 != responseID && 0 != responseID; }