diff --git a/Terminal.Gui/Core/Autocomplete/Autocomplete.cs b/Terminal.Gui/Core/Autocomplete/Autocomplete.cs
index f351b8424..61ccc01ea 100644
--- a/Terminal.Gui/Core/Autocomplete/Autocomplete.cs
+++ b/Terminal.Gui/Core/Autocomplete/Autocomplete.cs
@@ -324,6 +324,7 @@ namespace Terminal.Gui {
if (IsWordChar ((char)kb.Key)) {
Visible = true;
closed = false;
+ return false;
}
if (kb.Key == Reopen) {
@@ -332,6 +333,9 @@ namespace Terminal.Gui {
if (closed || Suggestions.Count == 0) {
Visible = false;
+ if (!closed) {
+ Close ();
+ }
return false;
}
@@ -345,6 +349,17 @@ namespace Terminal.Gui {
return true;
}
+ if (kb.Key == Key.CursorLeft || kb.Key == Key.CursorRight) {
+ GenerateSuggestions (kb.Key == Key.CursorLeft ? -1 : 1);
+ if (Suggestions.Count == 0) {
+ Visible = false;
+ if (!closed) {
+ Close ();
+ }
+ }
+ return false;
+ }
+
if (kb.Key == SelectionKey) {
return Select ();
}
@@ -368,6 +383,9 @@ namespace Terminal.Gui {
public virtual bool MouseEvent (MouseEvent me, bool fromHost = false)
{
if (fromHost) {
+ if (!Visible) {
+ return false;
+ }
GenerateSuggestions ();
if (Visible && Suggestions.Count == 0) {
Visible = false;
@@ -444,7 +462,8 @@ namespace Terminal.Gui {
/// Populates with all strings in that
/// match with the current cursor position/text in the
///
- public virtual void GenerateSuggestions ()
+ /// The column offset.
+ public virtual void GenerateSuggestions (int columnOffset = 0)
{
// if there is nothing to pick from
if (AllSuggestions.Count == 0) {
@@ -452,7 +471,7 @@ namespace Terminal.Gui {
return;
}
- var currentWord = GetCurrentWord ();
+ var currentWord = GetCurrentWord (columnOffset);
if (string.IsNullOrWhiteSpace (currentWord)) {
ClearSuggestions ();
@@ -524,11 +543,12 @@ namespace Terminal.Gui {
///
/// Returns the currently selected word from the .
///
- /// When overriding this method views can make use of
+ /// When overriding this method views can make use of
///
///
+ /// The column offset.
///
- protected abstract string GetCurrentWord ();
+ protected abstract string GetCurrentWord (int columnOffset = 0);
///
///
@@ -536,37 +556,40 @@ namespace Terminal.Gui {
/// or null. Also returns null if the is positioned in the middle of a word.
///
///
- /// Use this method to determine whether autocomplete should be shown when the cursor is at
- /// a given point in a line and to get the word from which suggestions should be generated.
+ ///
+ /// Use this method to determine whether autocomplete should be shown when the cursor is at
+ /// a given point in a line and to get the word from which suggestions should be generated.
+ /// Use the to indicate if search the word at left (negative),
+ /// at right (positive) or at the current column (zero) which is the default.
+ ///
///
///
///
+ ///
///
- protected virtual string IdxToWord (List line, int idx)
+ protected virtual string IdxToWord (List line, int idx, int columnOffset = 0)
{
StringBuilder sb = new StringBuilder ();
+ var endIdx = idx;
- // do not generate suggestions if the cursor is positioned in the middle of a word
- bool areMidWord;
-
- if (idx == line.Count) {
- // the cursor positioned at the very end of the line
- areMidWord = false;
- } else {
- // we are in the middle of a word if the cursor is over a letter/number
- areMidWord = IsWordChar (line [idx]);
+ // get the ending word index
+ while (endIdx < line.Count) {
+ if (IsWordChar (line [endIdx])) {
+ endIdx++;
+ } else {
+ break;
+ }
}
- // if we are in the middle of a word then there is no way to autocomplete that word
- if (areMidWord) {
+ // It isn't a word char then there is no way to autocomplete that word
+ if (endIdx == idx && columnOffset != 0) {
return null;
}
// we are at the end of a word. Work out what has been typed so far
- while (idx-- > 0) {
-
- if (IsWordChar (line [idx])) {
- sb.Insert (0, (char)line [idx]);
+ while (endIdx-- > 0) {
+ if (IsWordChar (line [endIdx])) {
+ sb.Insert (0, (char)line [endIdx]);
} else {
break;
}
diff --git a/Terminal.Gui/Core/Autocomplete/IAutocomplete.cs b/Terminal.Gui/Core/Autocomplete/IAutocomplete.cs
index 32e7046e7..2e3194eb3 100644
--- a/Terminal.Gui/Core/Autocomplete/IAutocomplete.cs
+++ b/Terminal.Gui/Core/Autocomplete/IAutocomplete.cs
@@ -109,6 +109,7 @@ namespace Terminal.Gui {
/// Populates with all strings in that
/// match with the current cursor position/text in the .
///
- void GenerateSuggestions ();
+ /// The column offset. Current (zero - default), left (negative), right (positive).
+ void GenerateSuggestions (int columnOffset = 0);
}
}
diff --git a/Terminal.Gui/Views/TextField.cs b/Terminal.Gui/Views/TextField.cs
index 14fc56394..07389fb4f 100644
--- a/Terminal.Gui/Views/TextField.cs
+++ b/Terminal.Gui/Views/TextField.cs
@@ -1346,12 +1346,12 @@ namespace Terminal.Gui {
}
///
- protected override string GetCurrentWord ()
+ protected override string GetCurrentWord (int columnOffset = 0)
{
var host = (TextField)HostControl;
var currentLine = host.Text.ToRuneList ();
- var cursorPosition = Math.Min (host.CursorPosition, currentLine.Count);
- return IdxToWord (currentLine, cursorPosition);
+ var cursorPosition = Math.Min (host.CursorPosition + columnOffset, currentLine.Count);
+ return IdxToWord (currentLine, cursorPosition, columnOffset);
}
///
diff --git a/Terminal.Gui/Views/TextView.cs b/Terminal.Gui/Views/TextView.cs
index dd609f12c..7cb797b11 100644
--- a/Terminal.Gui/Views/TextView.cs
+++ b/Terminal.Gui/Views/TextView.cs
@@ -2447,6 +2447,10 @@ namespace Terminal.Gui {
PositionCursor ();
+ if (clickWithSelecting) {
+ clickWithSelecting = false;
+ return;
+ }
if (SelectedLength > 0)
return;
@@ -2677,8 +2681,10 @@ namespace Terminal.Gui {
need = true;
} else if ((wordWrap && leftColumn > 0) || (dSize.size + RightOffset < Frame.Width + offB.width
&& tSize.size + RightOffset < Frame.Width + offB.width)) {
- leftColumn = 0;
- need = true;
+ if (leftColumn > 0) {
+ leftColumn = 0;
+ need = true;
+ }
}
if (currentRow < topRow) {
@@ -4279,6 +4285,7 @@ namespace Terminal.Gui {
}
bool isButtonShift;
+ bool clickWithSelecting;
///
public override bool MouseEvent (MouseEvent ev)
@@ -4372,6 +4379,7 @@ namespace Terminal.Gui {
columnTrack = currentColumn;
} else if (ev.Flags.HasFlag (MouseFlags.Button1Pressed)) {
if (shiftSelecting) {
+ clickWithSelecting = true;
StopSelecting ();
}
ProcessMouseClick (ev, out _);
@@ -4475,12 +4483,12 @@ namespace Terminal.Gui {
public class TextViewAutocomplete : Autocomplete {
///
- protected override string GetCurrentWord ()
+ protected override string GetCurrentWord (int columnOffset = 0)
{
var host = (TextView)HostControl;
var currentLine = host.GetCurrentLine ();
- var cursorPosition = Math.Min (host.CurrentColumn, currentLine.Count);
- return IdxToWord (currentLine, cursorPosition);
+ var cursorPosition = Math.Min (host.CurrentColumn + columnOffset, currentLine.Count);
+ return IdxToWord (currentLine, cursorPosition, columnOffset);
}
///
diff --git a/UnitTests/Views/AutocompleteTests.cs b/UnitTests/Views/AutocompleteTests.cs
index 51dd0076c..dca1fd81c 100644
--- a/UnitTests/Views/AutocompleteTests.cs
+++ b/UnitTests/Views/AutocompleteTests.cs
@@ -6,9 +6,16 @@ using System.Text.RegularExpressions;
using System.Threading.Tasks;
using Terminal.Gui;
using Xunit;
+using Xunit.Abstractions;
namespace Terminal.Gui.ViewTests {
public class AutocompleteTests {
+ readonly ITestOutputHelper output;
+
+ public AutocompleteTests (ITestOutputHelper output)
+ {
+ this.output = output;
+ }
[Fact]
public void Test_GenerateSuggestions_Simple ()
@@ -151,5 +158,84 @@ namespace Terminal.Gui.ViewTests {
Assert.Empty (tv.Autocomplete.Suggestions);
Assert.Equal (3, tv.Autocomplete.AllSuggestions.Count);
}
+
+ [Fact, AutoInitShutdown]
+ public void CursorLeft_CursorRight_Mouse_Button_Pressed_Does_Not_Show_Popup ()
+ {
+ var tv = new TextView () {
+ Width = 50,
+ Height = 5,
+ Text = "This a long line and against TextView."
+ };
+ tv.Autocomplete.AllSuggestions = Regex.Matches (tv.Text.ToString (), "\\w+")
+ .Select (s => s.Value)
+ .Distinct ().ToList ();
+ var top = Application.Top;
+ top.Add (tv);
+ Application.Begin (top);
+
+
+ for (int i = 0; i < 7; i++) {
+ Assert.True (tv.ProcessKey (new KeyEvent (Key.CursorRight, new KeyModifiers ())));
+ Application.Refresh ();
+ TestHelpers.AssertDriverContentsWithFrameAre (@"
+This a long line and against TextView.", output);
+ }
+
+ Assert.True (tv.MouseEvent (new MouseEvent () {
+ X = 6,
+ Y = 0,
+ Flags = MouseFlags.Button1Pressed
+ }));
+ Application.Refresh ();
+ TestHelpers.AssertDriverContentsWithFrameAre (@"
+This a long line and against TextView.", output);
+
+ Assert.True (tv.ProcessKey (new KeyEvent (Key.g, new KeyModifiers ())));
+ Application.Refresh ();
+ TestHelpers.AssertDriverContentsWithFrameAre (@"
+This ag long line and against TextView.
+ against ", output);
+
+ Assert.True (tv.ProcessKey (new KeyEvent (Key.CursorLeft, new KeyModifiers ())));
+ Application.Refresh ();
+ TestHelpers.AssertDriverContentsWithFrameAre (@"
+This ag long line and against TextView.
+ against ", output);
+
+ Assert.True (tv.ProcessKey (new KeyEvent (Key.CursorLeft, new KeyModifiers ())));
+ Application.Refresh ();
+ TestHelpers.AssertDriverContentsWithFrameAre (@"
+This ag long line and against TextView.
+ against ", output);
+
+ Assert.True (tv.ProcessKey (new KeyEvent (Key.CursorLeft, new KeyModifiers ())));
+ Application.Refresh ();
+ TestHelpers.AssertDriverContentsWithFrameAre (@"
+This ag long line and against TextView.", output);
+
+ for (int i = 0; i < 3; i++) {
+ Assert.True (tv.ProcessKey (new KeyEvent (Key.CursorRight, new KeyModifiers ())));
+ Application.Refresh ();
+ TestHelpers.AssertDriverContentsWithFrameAre (@"
+This ag long line and against TextView.", output);
+ }
+
+ Assert.True (tv.ProcessKey (new KeyEvent (Key.Backspace, new KeyModifiers ())));
+ Application.Refresh ();
+ TestHelpers.AssertDriverContentsWithFrameAre (@"
+This a long line and against TextView.", output);
+
+ Assert.True (tv.ProcessKey (new KeyEvent (Key.n, new KeyModifiers ())));
+ Application.Refresh ();
+ TestHelpers.AssertDriverContentsWithFrameAre (@"
+This an long line and against TextView.
+ and ", output);
+
+ Assert.True (tv.ProcessKey (new KeyEvent (Key.CursorRight, new KeyModifiers ())));
+ Application.Refresh ();
+ TestHelpers.AssertDriverContentsWithFrameAre (@"
+This an long line and against TextView.", output);
+ }
}
}
\ No newline at end of file