|
|
@ -1,9 +1,11 @@ |
|
|
|
using System; |
|
|
|
using System; |
|
|
|
using System.Diagnostics.CodeAnalysis; |
|
|
|
using System.Diagnostics.CodeAnalysis; |
|
|
|
using System.Linq; |
|
|
|
using System.Linq; |
|
|
|
|
|
|
|
using System.Threading.Tasks; |
|
|
|
using Avalonia; |
|
|
|
using Avalonia; |
|
|
|
using Avalonia.Controls; |
|
|
|
using Avalonia.Controls; |
|
|
|
using Avalonia.Input; |
|
|
|
using Avalonia.Input; |
|
|
|
|
|
|
|
using Avalonia.Threading; |
|
|
|
using Avalonia.Xaml.Interactivity; |
|
|
|
using Avalonia.Xaml.Interactivity; |
|
|
|
using AvaloniaEdit; |
|
|
|
using AvaloniaEdit; |
|
|
|
using AvaloniaEdit.Document; |
|
|
|
using AvaloniaEdit.Document; |
|
|
@ -74,6 +76,7 @@ public class TextEditorCompletionBehavior : Behavior<TextEditor> |
|
|
|
|
|
|
|
|
|
|
|
textEditor = editor; |
|
|
|
textEditor = editor; |
|
|
|
textEditor.TextArea.TextEntered += TextArea_TextEntered; |
|
|
|
textEditor.TextArea.TextEntered += TextArea_TextEntered; |
|
|
|
|
|
|
|
textEditor.TextArea.KeyDown += TextArea_KeyDown; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
protected override void OnDetaching() |
|
|
|
protected override void OnDetaching() |
|
|
@ -81,6 +84,7 @@ public class TextEditorCompletionBehavior : Behavior<TextEditor> |
|
|
|
base.OnDetaching(); |
|
|
|
base.OnDetaching(); |
|
|
|
|
|
|
|
|
|
|
|
textEditor.TextArea.TextEntered -= TextArea_TextEntered; |
|
|
|
textEditor.TextArea.TextEntered -= TextArea_TextEntered; |
|
|
|
|
|
|
|
textEditor.TextArea.KeyDown -= TextArea_KeyDown; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
private CompletionWindow CreateCompletionWindow(TextArea textArea) |
|
|
|
private CompletionWindow CreateCompletionWindow(TextArea textArea) |
|
|
@ -96,43 +100,16 @@ public class TextEditorCompletionBehavior : Behavior<TextEditor> |
|
|
|
return window; |
|
|
|
return window; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
private void TextArea_TextEntered(object? sender, TextInputEventArgs e) |
|
|
|
public void InvokeManualCompletion() |
|
|
|
{ |
|
|
|
{ |
|
|
|
Logger.ConditionalTrace($"Text entered: {e.Text.ToRepr()}"); |
|
|
|
if (CompletionProvider is null) |
|
|
|
|
|
|
|
|
|
|
|
if (!IsEnabled || CompletionProvider is null) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
Logger.ConditionalTrace("Skipping, not enabled"); |
|
|
|
|
|
|
|
return; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (e.Text is not { } triggerText) |
|
|
|
|
|
|
|
{ |
|
|
|
{ |
|
|
|
Logger.ConditionalTrace("Skipping, null trigger text"); |
|
|
|
throw new NullReferenceException("CompletionProvider is null"); |
|
|
|
return; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (!triggerText.All(IsCompletionChar)) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
if (completionWindow is { } window) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
// Disallowed chars, close completion window if its open |
|
|
|
|
|
|
|
Logger.ConditionalTrace( |
|
|
|
|
|
|
|
$"Closing completion window: '{triggerText}' not a valid completion char" |
|
|
|
|
|
|
|
); |
|
|
|
|
|
|
|
window.Close(); |
|
|
|
|
|
|
|
completionWindow = null; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
else |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
Logger.ConditionalTrace($"Skipping, invalid trigger text: {triggerText.ToRepr()}"); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
return; |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// If window already open, skip since handled by completion window |
|
|
|
// If window already open, skip since handled by completion window |
|
|
|
// Unless this is an end char, where we'll open a new window |
|
|
|
// Unless this is an end char, where we'll open a new window |
|
|
|
if (completionWindow != null && !triggerText.All(IsCompletionEndChar)) |
|
|
|
if (completionWindow is { IsOpen: true }) |
|
|
|
{ |
|
|
|
{ |
|
|
|
Logger.ConditionalTrace("Skipping, completion window already open"); |
|
|
|
Logger.ConditionalTrace("Skipping, completion window already open"); |
|
|
|
return; |
|
|
|
return; |
|
|
@ -159,7 +136,7 @@ public class TextEditorCompletionBehavior : Behavior<TextEditor> |
|
|
|
var tokenSegment = completionRequest.Segment; |
|
|
|
var tokenSegment = completionRequest.Segment; |
|
|
|
|
|
|
|
|
|
|
|
var token = textEditor.Document.GetText(tokenSegment); |
|
|
|
var token = textEditor.Document.GetText(tokenSegment); |
|
|
|
Logger.Trace("Using token {Token} ({@Segment})", token, tokenSegment); |
|
|
|
Logger.ConditionalTrace("Using token {Token} ({@Segment})", token, tokenSegment); |
|
|
|
|
|
|
|
|
|
|
|
completionWindow = CreateCompletionWindow(textEditor.TextArea); |
|
|
|
completionWindow = CreateCompletionWindow(textEditor.TextArea); |
|
|
|
completionWindow.StartOffset = tokenSegment.Offset; |
|
|
|
completionWindow.StartOffset = tokenSegment.Offset; |
|
|
@ -167,7 +144,7 @@ public class TextEditorCompletionBehavior : Behavior<TextEditor> |
|
|
|
|
|
|
|
|
|
|
|
completionWindow.UpdateQuery(completionRequest); |
|
|
|
completionWindow.UpdateQuery(completionRequest); |
|
|
|
|
|
|
|
|
|
|
|
completionWindow.Closed += delegate |
|
|
|
completionWindow.Closed += (_, _) => |
|
|
|
{ |
|
|
|
{ |
|
|
|
completionWindow = null; |
|
|
|
completionWindow = null; |
|
|
|
}; |
|
|
|
}; |
|
|
@ -175,6 +152,52 @@ public class TextEditorCompletionBehavior : Behavior<TextEditor> |
|
|
|
completionWindow.Show(); |
|
|
|
completionWindow.Show(); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private void TextArea_TextEntered(object? sender, TextInputEventArgs e) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
Logger.ConditionalTrace($"Text entered: {e.Text.ToRepr()}"); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (!IsEnabled || CompletionProvider is null) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
Logger.ConditionalTrace("Skipping, not enabled"); |
|
|
|
|
|
|
|
return; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (e.Text is not { } triggerText) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
Logger.ConditionalTrace("Skipping, null trigger text"); |
|
|
|
|
|
|
|
return; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (!triggerText.All(IsCompletionChar)) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
if (completionWindow is { } window) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
// Disallowed chars, close completion window if its open |
|
|
|
|
|
|
|
Logger.ConditionalTrace( |
|
|
|
|
|
|
|
$"Closing completion window: '{triggerText}' not a valid completion char" |
|
|
|
|
|
|
|
); |
|
|
|
|
|
|
|
window.Close(); |
|
|
|
|
|
|
|
completionWindow = null; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
else |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
Logger.ConditionalTrace($"Skipping, invalid trigger text: {triggerText.ToRepr()}"); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
return; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Dispatcher.UIThread.Post(InvokeManualCompletion, DispatcherPriority.Background); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private void TextArea_KeyDown(object? sender, KeyEventArgs e) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
if (e is { Key: Key.Space, KeyModifiers: KeyModifiers.Control }) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
Dispatcher.UIThread.Post(InvokeManualCompletion, DispatcherPriority.Background); |
|
|
|
|
|
|
|
e.Handled = true; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/// <summary> |
|
|
|
/// <summary> |
|
|
|
/// Highlights the text segment in the text editor |
|
|
|
/// Highlights the text segment in the text editor |
|
|
|
/// </summary> |
|
|
|
/// </summary> |
|
|
|