tooltip hover refinement
This commit is contained in:
@@ -14,6 +14,7 @@ using Microsoft.CodeAnalysis.MSBuild;
|
||||
using Microsoft.CodeAnalysis.Razor.SemanticTokens;
|
||||
using Microsoft.CodeAnalysis.Remote.Razor.ProjectSystem;
|
||||
using Microsoft.CodeAnalysis.Remote.Razor.SemanticTokens;
|
||||
using Microsoft.CodeAnalysis.Shared.Extensions;
|
||||
using Microsoft.CodeAnalysis.Shared.Utilities;
|
||||
using Microsoft.CodeAnalysis.Text;
|
||||
using SharpIDE.Application.Features.Analysis.FixLoaders;
|
||||
@@ -476,14 +477,14 @@ public static class RoslynAnalysis
|
||||
return changedFilesWithText;
|
||||
}
|
||||
|
||||
public static async Task<ISymbol?> LookupSymbol(SharpIdeFile fileModel, LinePosition linePosition)
|
||||
public static async Task<(ISymbol?, LinePositionSpan?)> LookupSymbol(SharpIdeFile fileModel, LinePosition linePosition)
|
||||
{
|
||||
await _solutionLoadedTcs.Task;
|
||||
var symbol = fileModel.IsRazorFile ? await LookupSymbolInRazor(fileModel, linePosition) : await LookupSymbolInCs(fileModel, linePosition);
|
||||
return symbol;
|
||||
var (symbol, linePositionSpan) = fileModel.IsRazorFile ? await LookupSymbolInRazor(fileModel, linePosition) : await LookupSymbolInCs(fileModel, linePosition);
|
||||
return (symbol, linePositionSpan);
|
||||
}
|
||||
|
||||
private static async Task<ISymbol?> LookupSymbolInRazor(SharpIdeFile fileModel, LinePosition linePosition, CancellationToken cancellationToken = default)
|
||||
private static async Task<(ISymbol? symbol, LinePositionSpan? linePositionSpan)> LookupSymbolInRazor(SharpIdeFile fileModel, LinePosition linePosition, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var sharpIdeProjectModel = ((IChildSharpIdeNode) fileModel).GetNearestProjectNode()!;
|
||||
var project = _workspace!.CurrentSolution.Projects.Single(s => s.FilePath == sharpIdeProjectModel!.FilePath);
|
||||
@@ -502,11 +503,11 @@ public static class RoslynAnalysis
|
||||
|
||||
var mappedPosition = MapRazorLinePositionToGeneratedCSharpAbsolutePosition(razorCSharpDocument, razorText, linePosition);
|
||||
var semanticModelAsync = await generatedDocument.GetSemanticModelAsync(cancellationToken);
|
||||
var symbol = GetSymbolAtPosition(semanticModelAsync!, generatedDocSyntaxRoot!, mappedPosition!.Value);
|
||||
return symbol;
|
||||
var (symbol, linePositionSpan) = GetSymbolAtPosition(semanticModelAsync!, generatedDocSyntaxRoot!, mappedPosition!.Value);
|
||||
return (symbol, linePositionSpan);
|
||||
}
|
||||
|
||||
private static async Task<ISymbol?> LookupSymbolInCs(SharpIdeFile fileModel, LinePosition linePosition)
|
||||
private static async Task<(ISymbol? symbol, LinePositionSpan? linePositionSpan)> LookupSymbolInCs(SharpIdeFile fileModel, LinePosition linePosition)
|
||||
{
|
||||
var project = _workspace!.CurrentSolution.Projects.Single(s => s.FilePath == ((IChildSharpIdeNode)fileModel).GetNearestProjectNode()!.FilePath);
|
||||
var document = project.Documents.Single(s => s.FilePath == fileModel.Path);
|
||||
@@ -519,18 +520,19 @@ public static class RoslynAnalysis
|
||||
return GetSymbolAtPosition(semanticModel, syntaxRoot!, position);
|
||||
}
|
||||
|
||||
private static ISymbol? GetSymbolAtPosition(SemanticModel semanticModel, SyntaxNode root, int position)
|
||||
private static (ISymbol? symbol, LinePositionSpan? linePositionSpan) GetSymbolAtPosition(SemanticModel semanticModel, SyntaxNode root, int position)
|
||||
{
|
||||
var node = root.FindToken(position).Parent!;
|
||||
var symbol = semanticModel.GetSymbolInfo(node).Symbol ?? semanticModel.GetDeclaredSymbol(node);
|
||||
if (symbol is null)
|
||||
{
|
||||
Console.WriteLine("No symbol found at position");
|
||||
return null;
|
||||
return (null, null);
|
||||
}
|
||||
|
||||
var linePositionSpan = root.SyntaxTree.GetLineSpan(node.Span).Span;
|
||||
Console.WriteLine($"Symbol found: {symbol.Name} ({symbol.Kind}) - {symbol.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat)}");
|
||||
return symbol;
|
||||
return (symbol, linePositionSpan);
|
||||
}
|
||||
|
||||
private static int? MapRazorLinePositionToGeneratedCSharpAbsolutePosition(RazorCSharpDocument razorCSharpDocument, SourceText razorText, LinePosition razorLinePosition)
|
||||
|
||||
@@ -88,16 +88,40 @@ public partial class SharpIdeCodeEdit : CodeEdit
|
||||
SetSymbolLookupWordAsValid(true);
|
||||
}
|
||||
|
||||
// This method is a bit of a disaster - we create an additional invisible Window, so that the tooltip window doesn't disappear while the mouse is over the hovered symbol
|
||||
private async void OnSymbolHovered(string symbol, long line, long column)
|
||||
{
|
||||
if (HasFocus() is false) return; // only show if we have focus, every tab is currently listening for this event, maybe find a better way
|
||||
var globalMousePosition = GetGlobalMousePosition(); // don't breakpoint before this, else your mouse position will be wrong
|
||||
var lineHeight = GetLineHeight();
|
||||
GD.Print($"Symbol hovered: {symbol} at line {line}, column {column}");
|
||||
|
||||
var roslynSymbol = await RoslynAnalysis.LookupSymbol(_currentFile, new LinePosition((int)line, (int)column));
|
||||
if (roslynSymbol is null)
|
||||
var (roslynSymbol, linePositionSpan) = await RoslynAnalysis.LookupSymbol(_currentFile, new LinePosition((int)line, (int)column));
|
||||
if (roslynSymbol is null || linePositionSpan is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var symbolNameHoverWindow = new Window();
|
||||
symbolNameHoverWindow.WrapControls = true;
|
||||
symbolNameHoverWindow.Unresizable = true;
|
||||
symbolNameHoverWindow.Transparent = true;
|
||||
symbolNameHoverWindow.Borderless = true;
|
||||
symbolNameHoverWindow.PopupWMHint = true;
|
||||
symbolNameHoverWindow.MinimizeDisabled = true;
|
||||
symbolNameHoverWindow.MaximizeDisabled = true;
|
||||
|
||||
var startSymbolCharRect = GetRectAtLineColumn(linePositionSpan.Value.Start.Line, linePositionSpan.Value.Start.Character + 1);
|
||||
var endSymbolCharRect = GetRectAtLineColumn(linePositionSpan.Value.End.Line, linePositionSpan.Value.End.Character + 1);
|
||||
symbolNameHoverWindow.Size = new Vector2I(endSymbolCharRect.End.X - startSymbolCharRect.Position.X, lineHeight);
|
||||
|
||||
var globalPosition = GetGlobalPosition();
|
||||
var startSymbolCharGlobalPos = startSymbolCharRect.Position + globalPosition;
|
||||
var endSymbolCharGlobalPos = endSymbolCharRect.Position + globalPosition;
|
||||
|
||||
AddChild(symbolNameHoverWindow);
|
||||
symbolNameHoverWindow.Position = new Vector2I((int)startSymbolCharGlobalPos.X, (int)endSymbolCharGlobalPos.Y);
|
||||
symbolNameHoverWindow.Popup();
|
||||
|
||||
var tooltipWindow = new Window();
|
||||
tooltipWindow.WrapControls = true;
|
||||
@@ -108,12 +132,18 @@ public partial class SharpIdeCodeEdit : CodeEdit
|
||||
tooltipWindow.MinimizeDisabled = true;
|
||||
tooltipWindow.MaximizeDisabled = true;
|
||||
|
||||
var timer = new Timer { WaitTime = 0.5f, OneShot = true, Autostart = false };
|
||||
var timer = new Timer { WaitTime = 0.05f, OneShot = true, Autostart = false };
|
||||
tooltipWindow.AddChild(timer);
|
||||
timer.Timeout += () => tooltipWindow.QueueFree();
|
||||
timer.Timeout += () =>
|
||||
{
|
||||
tooltipWindow.QueueFree();
|
||||
symbolNameHoverWindow.QueueFree();
|
||||
};
|
||||
|
||||
tooltipWindow.MouseExited += () => timer.Start();
|
||||
tooltipWindow.MouseEntered += () => timer.Stop();
|
||||
symbolNameHoverWindow.MouseExited += () => timer.Start();
|
||||
symbolNameHoverWindow.MouseEntered += () => timer.Stop();
|
||||
|
||||
var styleBox = new StyleBoxFlat
|
||||
{
|
||||
@@ -154,17 +184,12 @@ public partial class SharpIdeCodeEdit : CodeEdit
|
||||
panel.AddChild(symbolInfoNode);
|
||||
var vboxContainer = new VBoxContainer();
|
||||
vboxContainer.AddThemeConstantOverride("separation", 0);
|
||||
vboxContainer.AddChild(new Control { CustomMinimumSize = new Vector2I(0, GetLineHeight()) });
|
||||
vboxContainer.AddChild(panel);
|
||||
tooltipWindow.AddChild(vboxContainer);
|
||||
tooltipWindow.ChildControlsChanged();
|
||||
AddChild(tooltipWindow);
|
||||
|
||||
var globalSymbolPosition = GetRectAtLineColumn((int)line, (int)column).Position + GetGlobalPosition();
|
||||
|
||||
var globalMousePosition = GetGlobalMousePosition();
|
||||
// -1 so that the mouse is inside the popup, otherwise the mouse exit event won't occur if the cursor moves away immediately
|
||||
tooltipWindow.Position = new Vector2I((int)globalMousePosition.X - 1, (int)globalSymbolPosition.Y);
|
||||
tooltipWindow.Position = new Vector2I((int)globalMousePosition.X, (int)startSymbolCharGlobalPos.Y + lineHeight);
|
||||
tooltipWindow.Popup();
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user