Fix auto complete not working in middle of text

This commit is contained in:
Stone_Red
2026-08-02 23:55:24 +02:00
parent 433de54d45
commit b712ca6cc9
3 changed files with 35 additions and 11 deletions
@@ -31,7 +31,9 @@ public sealed class AutocompleteController(IEnumerable<AutocompleteProvider> pro
public int TriggerCharIndex { get; private set; } public int TriggerCharIndex { get; private set; }
public void Update(string text) public int WordEndIndex { get; private set; }
public void Update(string text, int caretIndex)
{ {
if (string.IsNullOrEmpty(text) || _providers.Count == 0) if (string.IsNullOrEmpty(text) || _providers.Count == 0)
{ {
@@ -39,7 +41,8 @@ public sealed class AutocompleteController(IEnumerable<AutocompleteProvider> pro
return; return;
} }
Match match = TriggerPattern.Match(text); int clampedCaret = Math.Clamp(caretIndex, 0, text.Length);
Match match = TriggerPattern.Match(text[..clampedCaret]);
if (!match.Success) if (!match.Success)
{ {
Reset(); Reset();
@@ -58,6 +61,7 @@ public sealed class AutocompleteController(IEnumerable<AutocompleteProvider> pro
ActiveProvider = provider; ActiveProvider = provider;
FilterText = filter; FilterText = filter;
TriggerCharIndex = match.Index; TriggerCharIndex = match.Index;
WordEndIndex = match.Index + match.Length;
List<string> items = provider.ItemsSource().ToList(); List<string> items = provider.ItemsSource().ToList();
List<string> filtered = items List<string> filtered = items
@@ -106,8 +106,6 @@ public sealed class MessageComposerViewModel : ViewModelBase
(draft, hasFiles, isReadOnly) => (draft, hasFiles, isReadOnly) =>
(!string.IsNullOrWhiteSpace(draft) || hasFiles) && !isReadOnly); (!string.IsNullOrWhiteSpace(draft) || hasFiles) && !isReadOnly);
SendCommand = ReactiveCommand.Create(Send, canSend); SendCommand = ReactiveCommand.Create(Send, canSend);
_ = this.WhenAnyValue(x => x.Draft).Subscribe(OnDraftChanged);
} }
public void UpdateAvailableUsers(IEnumerable<UserViewModel> users) public void UpdateAvailableUsers(IEnumerable<UserViewModel> users)
@@ -179,17 +177,24 @@ public sealed class MessageComposerViewModel : ViewModelBase
return _commandHandler?.IsCommand(input) ?? input.StartsWith('/'); return _commandHandler?.IsCommand(input) ?? input.StartsWith('/');
} }
public void InsertAutocomplete(string item) public int InsertAutocomplete(string item)
{ {
string? insertion = Autocomplete.GetInsertion(item); string? insertion = Autocomplete.GetInsertion(item);
if (insertion is null) if (insertion is null)
{ {
return; return -1;
} }
string before = Draft[..Autocomplete.TriggerCharIndex]; string before = Draft[..Autocomplete.TriggerCharIndex];
Draft = $"{before}{insertion}"; string after = Draft[Autocomplete.WordEndIndex..];
if (after.Length > 0 && char.IsWhiteSpace(after[0]))
{
insertion = insertion.TrimEnd();
}
Draft = $"{before}{insertion}{after}";
Autocomplete.Reset(); Autocomplete.Reset();
return before.Length + insertion.Length;
} }
public void ClearReplyTarget() public void ClearReplyTarget()
@@ -226,8 +231,8 @@ public sealed class MessageComposerViewModel : ViewModelBase
: string.Empty; : string.Empty;
} }
private void OnDraftChanged(string? draft) public void UpdateAutocomplete(string text, int caretIndex)
{ {
Autocomplete.Update(draft ?? string.Empty); Autocomplete.Update(text, caretIndex);
} }
} }
+17 -2
View File
@@ -1,3 +1,4 @@
using Avalonia;
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Input; using Avalonia.Input;
using Avalonia.Interactivity; using Avalonia.Interactivity;
@@ -29,6 +30,11 @@ public partial class MessageComposerView : UserControl
// Intercept Enter before the TextBox processes it (AcceptsReturn=True would insert a newline) // Intercept Enter before the TextBox processes it (AcceptsReturn=True would insert a newline)
MessageTextBox.AddHandler(InputElement.KeyDownEvent, OnTextBoxTunnelingKeyDown, RoutingStrategies.Tunnel); MessageTextBox.AddHandler(InputElement.KeyDownEvent, OnTextBoxTunnelingKeyDown, RoutingStrategies.Tunnel);
MessageTextBox.GetObservable(TextBox.TextProperty)
.Subscribe(_ => UpdateAutocomplete(MessageTextBox.Text ?? string.Empty, MessageTextBox.CaretIndex));
MessageTextBox.GetObservable(TextBox.CaretIndexProperty)
.Subscribe(_ => UpdateAutocomplete(MessageTextBox.Text ?? string.Empty, MessageTextBox.CaretIndex));
_spellChecker = new TextBoxSpellChecker(SpellCheckerConfig.Create("en_GB")); _spellChecker = new TextBoxSpellChecker(SpellCheckerConfig.Create("en_GB"));
_spellChecker.Initialize(MessageTextBox); _spellChecker.Initialize(MessageTextBox);
} }
@@ -47,6 +53,12 @@ public partial class MessageComposerView : UserControl
} }
} }
private void UpdateAutocomplete(string text, int caretIndex)
{
MessageComposerViewModel? vm = this.GetDataContext<MessageComposerViewModel>();
vm?.UpdateAutocomplete(text, caretIndex);
}
private void OnTextBoxKeyDown(object? sender, KeyEventArgs e) private void OnTextBoxKeyDown(object? sender, KeyEventArgs e)
{ {
MessageComposerViewModel? vm = this.GetDataContext<MessageComposerViewModel>(); MessageComposerViewModel? vm = this.GetDataContext<MessageComposerViewModel>();
@@ -137,8 +149,11 @@ public partial class MessageComposerView : UserControl
return; return;
} }
vm.InsertAutocomplete(item); int newCaret = vm.InsertAutocomplete(item);
MessageTextBox.CaretIndex = MessageTextBox.Text?.Length ?? 0; if (newCaret >= 0)
{
MessageTextBox.CaretIndex = newCaret;
}
_ = MessageTextBox.Focus(); _ = MessageTextBox.Focus();
} }