mirror of
https://github.com/SpartanJ/eepp.git
synced 2026-10-27 08:37:58 +03:00
eepp: Improved PopUpMenu positioning.
ecode: Added quick-fix support.
This commit is contained in:
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 };
|
||||
|
||||
@@ -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; }
|
||||
|
||||
Reference in New Issue
Block a user