瀏覽代碼

WIP:Start refactoring Autocomplete
- Seperate out suggestion generation from popup/append UI
- Reduce coupling and subclassing

Thomas 2 年之前
父節點
當前提交
e62e2382de

+ 0 - 14
Terminal.Gui/Core/Autocomplete/AppendAutocomplete.cs

@@ -22,14 +22,6 @@ namespace Terminal.Gui {
 			textField.SetNeedsDisplay ();
 		}
 
-		public override void GenerateSuggestions (int columnOffset = 0)
-		{
-			// TODO: testing only
-			AllSuggestions = new List<string> { "fish", "flipper", "fun" };
-
-			base.GenerateSuggestions();
-		}
-
 		public override bool MouseEvent (MouseEvent me, bool fromHost = false)
 		{
 			return false;
@@ -134,11 +126,5 @@ namespace Terminal.Gui {
 			textField.SetNeedsDisplay ();
 			return true;
 		}
-
-		protected override string GetCurrentWord (int columnOffset = 0)
-		{
-			// TODO: Make real
-			return textField.Text.ToString();
-		}
 	}
 }

+ 103 - 87
Terminal.Gui/Core/Autocomplete/AutocompleteBase.cs

@@ -6,111 +6,49 @@ using System.Text;
 using Rune = System.Rune;
 
 namespace Terminal.Gui {
-	public abstract class AutocompleteBase : IAutocomplete
+
+	public interface ISuggestionGenerator
 	{
-		public abstract View HostControl { get; set; }
-		public bool PopupInsideContainer { get; set; }
 
 		/// <summary>
-		/// The maximum width of the autocomplete dropdown
+		/// Populates <see cref="Suggestions"/> with all strings in <see cref="AllSuggestions"/> that
+		/// match with the current cursor position/text in the <see cref="HostControl"/>.
 		/// </summary>
-		public virtual int MaxWidth { get; set; } = 10;
+		/// <param name="columnOffset">The column offset. Current (zero - default), left (negative), right (positive).</param>
+		IEnumerable<string> GenerateSuggestions (List<Rune> currentLine, int idx);
 
-		/// <summary>
-		/// The maximum number of visible rows in the autocomplete dropdown to render
-		/// </summary>
-		public virtual int MaxHeight { get; set; } = 6;
+		bool IsWordChar (Rune rune);
 
-		/// <inheritdoc/>
-
-
-		/// <summary>
-		/// True if the autocomplete should be considered open and visible
-		/// </summary>
-		public virtual bool Visible { get; set; }
-
-		/// <summary>
-		/// The strings that form the current list of suggestions to render
-		/// based on what the user has typed so far.
-		/// </summary>
-		public virtual ReadOnlyCollection<string> Suggestions { get; set; } = new ReadOnlyCollection<string> (new string [0]);
+	}
 
+	public class SingleWordSuggestionGenerator : ISuggestionGenerator
+	{
 		/// <summary>
 		/// The full set of all strings that can be suggested.
 		/// </summary>
 		/// <returns></returns>
 		public virtual List<string> AllSuggestions { get; set; } = new List<string> ();
 
-		/// <summary>
-		/// The currently selected index into <see cref="Suggestions"/> that the user has highlighted
-		/// </summary>
-		public virtual int SelectedIdx { get; set; }
-
-
-		/// <inheritdoc/>
-		public abstract ColorScheme ColorScheme { get; set; }
-
-		/// <inheritdoc/>
-		public virtual Key SelectionKey { get; set; } = Key.Enter;
-
-		/// <inheritdoc/>
-		public virtual Key CloseKey { get; set; } = Key.Esc;
-
-		/// <inheritdoc/>
-		public virtual Key Reopen { get; set; } = Key.Space | Key.CtrlMask | Key.AltMask;
-
-		/// <inheritdoc/>
-		public abstract bool MouseEvent (MouseEvent me, bool fromHost = false);
-
-		/// <inheritdoc/>
-		public abstract bool ProcessKey (KeyEvent kb);
-		/// <inheritdoc/>
-		public abstract void RenderOverlay (Point renderAt);
-
-		/// <summary>
-		/// Clears <see cref="Suggestions"/>
-		/// </summary>
-		public virtual void ClearSuggestions ()
-		{
-			Suggestions = Enumerable.Empty<string> ().ToList ().AsReadOnly ();
-		}
-
-
-		/// <summary>
-		/// Populates <see cref="Suggestions"/> with all strings in <see cref="AllSuggestions"/> that
-		/// match with the current cursor position/text in the <see cref="HostControl"/>
-		/// </summary>
-		/// <param name="columnOffset">The column offset.</param>
-		public virtual void GenerateSuggestions (int columnOffset = 0)
+		public IEnumerable<string> GenerateSuggestions (List<Rune> currentLine, int idx)
 		{
 			// if there is nothing to pick from
 			if (AllSuggestions.Count == 0) {
-				ClearSuggestions ();
-				return;
+				return Enumerable.Empty<string>();
 			}
 
-			var currentWord = GetCurrentWord (columnOffset);
+			var currentWord = IdxToWord (currentLine, idx);
 
 			if (string.IsNullOrWhiteSpace (currentWord)) {
-				ClearSuggestions ();
+				return Enumerable.Empty<string>();
 			} else {
-				Suggestions = AllSuggestions.Where (o =>
+				return AllSuggestions.Where (o =>
 				o.StartsWith (currentWord, StringComparison.CurrentCultureIgnoreCase) &&
 				!o.Equals (currentWord, StringComparison.CurrentCultureIgnoreCase)
 				).ToList ().AsReadOnly ();
 
-				EnsureSelectedIdxIsValid ();
 			}
 		}
 
-		/// <summary>
-		/// Updates <see cref="SelectedIdx"/> to be a valid index within <see cref="Suggestions"/>
-		/// </summary>
-		public virtual void EnsureSelectedIdxIsValid ()
-		{
-			SelectedIdx = Math.Max (0, Math.Min (Suggestions.Count - 1, SelectedIdx));
-		}
-
 		/// <summary>
 		/// Return true if the given symbol should be considered part of a word
 		/// and can be contained in matches.  Base behavior is to use <see cref="char.IsLetterOrDigit(char)"/>
@@ -123,16 +61,6 @@ namespace Terminal.Gui {
 		}
 
 
-		/// <summary>
-		/// Returns the currently selected word from the <see cref="HostControl"/>.
-		/// <para>
-		/// When overriding this method views can make use of <see cref="IdxToWord(List{Rune}, int, int)"/>
-		/// </para>
-		/// </summary>
-		/// <param name="columnOffset">The column offset.</param>
-		/// <returns></returns>
-		protected abstract string GetCurrentWord (int columnOffset = 0);
-
 		/// <summary>
 		/// <para>
 		/// Given a <paramref name="line"/> of characters, returns the word which ends at <paramref name="idx"/> 
@@ -180,5 +108,93 @@ namespace Terminal.Gui {
 			return sb.ToString ();
 		}
 	}
+
+	public abstract class AutocompleteBase : IAutocomplete {
+		public abstract View HostControl { get; set; }
+		public bool PopupInsideContainer { get; set; }
+		
+		public ISuggestionGenerator SuggestionGenerator {get;set;} = new SingleWordSuggestionGenerator();
+
+		/// <summary>
+		/// The maximum width of the autocomplete dropdown
+		/// </summary>
+		public virtual int MaxWidth { get; set; } = 10;
+
+		/// <summary>
+		/// The maximum number of visible rows in the autocomplete dropdown to render
+		/// </summary>
+		public virtual int MaxHeight { get; set; } = 6;
+
+		/// <inheritdoc/>
+
+
+		/// <summary>
+		/// True if the autocomplete should be considered open and visible
+		/// </summary>
+		public virtual bool Visible { get; set; }
+
+		/// <summary>
+		/// The strings that form the current list of suggestions to render
+		/// based on what the user has typed so far.
+		/// </summary>
+		public virtual ReadOnlyCollection<string> Suggestions { get; set; } = new ReadOnlyCollection<string> (new string [0]);
+
+
+
+		/// <summary>
+		/// The currently selected index into <see cref="Suggestions"/> that the user has highlighted
+		/// </summary>
+		public virtual int SelectedIdx { get; set; }
+
+
+		/// <inheritdoc/>
+		public abstract ColorScheme ColorScheme { get; set; }
+
+		/// <inheritdoc/>
+		public virtual Key SelectionKey { get; set; } = Key.Enter;
+
+		/// <inheritdoc/>
+		public virtual Key CloseKey { get; set; } = Key.Esc;
+
+		/// <inheritdoc/>
+		public virtual Key Reopen { get; set; } = Key.Space | Key.CtrlMask | Key.AltMask;
+
+		/// <inheritdoc/>
+		public abstract bool MouseEvent (MouseEvent me, bool fromHost = false);
+
+		/// <inheritdoc/>
+		public abstract bool ProcessKey (KeyEvent kb);
+		/// <inheritdoc/>
+		public abstract void RenderOverlay (Point renderAt);
+
+		/// <summary>
+		/// Clears <see cref="Suggestions"/>
+		/// </summary>
+		public virtual void ClearSuggestions ()
+		{
+			Suggestions = Enumerable.Empty<string> ().ToList ().AsReadOnly ();
+		}
+
+
+		/// <summary>
+		/// Populates <see cref="Suggestions"/> with all strings in <see cref="AllSuggestions"/> that
+		/// match with the current cursor position/text in the <see cref="HostControl"/>
+		/// </summary>
+		/// <param name="columnOffset">The column offset.</param>
+		public virtual void GenerateSuggestions (List<Rune> currentLine, int idx)
+		{
+			Suggestions = SuggestionGenerator.GenerateSuggestions(currentLine, idx).ToList().AsReadOnly();
+
+			EnsureSelectedIdxIsValid ();
+		}
+
+		/// <summary>
+		/// Updates <see cref="SelectedIdx"/> to be a valid index within <see cref="Suggestions"/>
+		/// </summary>
+		public virtual void EnsureSelectedIdxIsValid ()
+		{
+			SelectedIdx = Math.Max (0, Math.Min (Suggestions.Count - 1, SelectedIdx));
+		}
+	}
 }
 

+ 3 - 11
Terminal.Gui/Core/Autocomplete/IAutocomplete.cs

@@ -42,11 +42,6 @@ namespace Terminal.Gui {
 		/// </summary>
 		ReadOnlyCollection<string> Suggestions { get; set; }
 
-		/// <summary>
-		/// The full set of all strings that can be suggested.
-		/// </summary>
-		List<string> AllSuggestions { get; set; }
-
 		/// <summary>
 		/// The currently selected index into <see cref="Suggestions"/> that the user has highlighted
 		/// </summary>
@@ -105,11 +100,8 @@ namespace Terminal.Gui {
 		/// </summary>
 		void ClearSuggestions ();
 
-		/// <summary>
-		/// Populates <see cref="Suggestions"/> with all strings in <see cref="AllSuggestions"/> that
-		/// match with the current cursor position/text in the <see cref="HostControl"/>.
-		/// </summary>
-		/// <param name="columnOffset">The column offset. Current (zero - default), left (negative), right (positive).</param>
-		void GenerateSuggestions (int columnOffset = 0);
+		ISuggestionGenerator SuggestionGenerator {get;set;}
+
+		void GenerateSuggestions (List<Rune> currentLine, int idx);
 	}
 }

+ 16 - 8
Terminal.Gui/Core/Autocomplete/PopupAutocomplete.cs

@@ -272,7 +272,7 @@ namespace Terminal.Gui {
 		/// <returns><c>true</c>if the key can be handled <c>false</c>otherwise.</returns>
 		public override bool ProcessKey (KeyEvent kb)
 		{
-			if (IsWordChar ((char)kb.Key)) {
+			if (SuggestionGenerator.IsWordChar ((char)kb.Key)) {
 				Visible = true;
 				ManipulatePopup ();
 				closed = false;
@@ -301,7 +301,8 @@ namespace Terminal.Gui {
 				return true;
 			}
 
-			if (kb.Key == Key.CursorLeft || kb.Key == Key.CursorRight) {
+			// TODO : Revisit this
+			/*if (kb.Key == Key.CursorLeft || kb.Key == Key.CursorRight) {
 				GenerateSuggestions (kb.Key == Key.CursorLeft ? -1 : 1);
 				if (Suggestions.Count == 0) {
 					Visible = false;
@@ -310,7 +311,7 @@ namespace Terminal.Gui {
 					}
 				}
 				return false;
-			}
+			}*/
 
 			if (kb.Key == SelectionKey) {
 				return Select ();
@@ -338,7 +339,10 @@ namespace Terminal.Gui {
 				if (!Visible) {
 					return false;
 				}
-				GenerateSuggestions ();
+				
+				// TODO: Revisit this
+				//GenerateSuggestions ();
+
 				if (Visible && Suggestions.Count == 0) {
 					Visible = false;
 					HostControl?.SetNeedsDisplay ();
@@ -430,7 +434,8 @@ namespace Terminal.Gui {
 		/// <returns>True if the insertion was possible otherwise false</returns>
 		protected virtual bool InsertSelection (string accepted)
 		{
-			var typedSoFar = GetCurrentWord () ?? "";
+			// TODO: Revisit this
+			/*var typedSoFar = GetCurrentWord () ?? "";
 
 			if (typedSoFar.Length < accepted.Length) {
 
@@ -438,12 +443,13 @@ namespace Terminal.Gui {
 				for (int i = 0; i < typedSoFar.Length; i++) {
 					DeleteTextBackwards ();
 				}
+				*/
 
 				InsertText (accepted);
 				return true;
-			}
+			//}
 
-			return false;
+			//return false;
 		}
 
 
@@ -502,7 +508,9 @@ namespace Terminal.Gui {
 		/// <returns></returns>
 		protected bool ReopenSuggestions ()
 		{
-			GenerateSuggestions ();
+			// TODO: Revisit
+			//GenerateSuggestions ();
+
 			if (Suggestions.Count > 0) {
 				Visible = true;
 				closed = false;

+ 10 - 10
Terminal.Gui/Views/TextField.cs

@@ -471,8 +471,9 @@ namespace Terminal.Gui {
 			if (SelectedLength > 0)
 				return;
 
+
 			// draw autocomplete
-			Autocomplete.GenerateSuggestions ();
+			GenerateSuggestions ();
 
 			var renderAt = new Point (
 				CursorPosition - ScrollOffset, 0);
@@ -480,6 +481,14 @@ namespace Terminal.Gui {
 			Autocomplete.RenderOverlay (renderAt);
 		}
 
+		private void GenerateSuggestions ()
+		{
+			var currentLine = Text.ToRuneList ();
+			var cursorPosition = Math.Min (this.CursorPosition, currentLine.Count);
+
+			Autocomplete.GenerateSuggestions(currentLine,cursorPosition);
+		}
+
 		/// <inheritdoc/>
 		public override Attribute GetNormalColor ()
 		{
@@ -1323,15 +1332,6 @@ namespace Terminal.Gui {
 			((TextField)HostControl).DeleteCharLeft (false);
 		}
 
-		/// <inheritdoc/>
-		protected override string GetCurrentWord (int columnOffset = 0)
-		{
-			var host = (TextField)HostControl;
-			var currentLine = host.Text.ToRuneList ();
-			var cursorPosition = Math.Min (host.CursorPosition + columnOffset, currentLine.Count);
-			return IdxToWord (currentLine, cursorPosition, columnOffset);
-		}
-
 		/// <inheritdoc/>
 		protected override void InsertText (string accepted)
 		{

+ 8 - 10
Terminal.Gui/Views/TextView.cs

@@ -2425,7 +2425,7 @@ namespace Terminal.Gui {
 				return;
 
 			// draw autocomplete
-			Autocomplete.GenerateSuggestions ();
+			GenerateSuggestions ();
 
 			var renderAt = new Point (
 				CursorPosition.X - LeftColumn,
@@ -2436,6 +2436,13 @@ namespace Terminal.Gui {
 			Autocomplete.RenderOverlay (renderAt);
 		}
 
+		private void GenerateSuggestions ()
+		{
+			var currentLine = this.GetCurrentLine ();
+			var cursorPosition = Math.Min (this.CurrentColumn, currentLine.Count);
+			Autocomplete.GenerateSuggestions(currentLine,cursorPosition);
+		}
+
 		/// <inheritdoc/>
 		public override Attribute GetNormalColor ()
 		{
@@ -4425,15 +4432,6 @@ namespace Terminal.Gui {
 	/// </summary>
 	public class TextViewAutocomplete : PopupAutocomplete {
 
-		///<inheritdoc/>
-		protected override string GetCurrentWord (int columnOffset = 0)
-		{
-			var host = (TextView)HostControl;
-			var currentLine = host.GetCurrentLine ();
-			var cursorPosition = Math.Min (host.CurrentColumn + columnOffset, currentLine.Count);
-			return IdxToWord (currentLine, cursorPosition, columnOffset);
-		}
-
 		/// <inheritdoc/>
 		protected override void DeleteTextBackwards ()
 		{

+ 24 - 0
UnitTests/Views/AutocompleteAppendTests

@@ -0,0 +1,24 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Xunit;
+using Xunit.Abstractions;
+
+namespace Terminal.Gui.ViewTests {
+	public class AutocomplteAppendTests {
+		readonly ITestOutputHelper output;
+
+		public AutocomplteAppendTests (ITestOutputHelper output)
+		{
+			this.output = output;
+		}
+
+        [Fact]
+        public void TestRender()
+        {
+            
+        }
+    }
+}