diff --git a/Terminal.Gui/Views/TextView.cs b/Terminal.Gui/Views/TextView.cs
index d533e24d1..b2444c75e 100644
--- a/Terminal.Gui/Views/TextView.cs
+++ b/Terminal.Gui/Views/TextView.cs
@@ -1333,17 +1333,46 @@ namespace Terminal.Gui {
}
}
- void ColorNormal ()
+ ///
+ /// Sets the driver to the default color for the control where no text is being rendered. Defaults to .
+ ///
+ protected virtual void ColorNormal ()
{
Driver.SetAttribute (ColorScheme.Normal);
}
- void ColorSelection ()
+ ///
+ /// Sets the to an appropriate color for rendering the given of the
+ /// current . Override to provide custom coloring by calling
+ /// Defaults to .
+ ///
+ ///
+ ///
+ protected virtual void ColorNormal (List line, int idx)
+ {
+ Driver.SetAttribute (ColorScheme.Normal);
+ }
+
+ ///
+ /// Sets the to an appropriate color for rendering the given of the
+ /// current . Override to provide custom coloring by calling
+ /// Defaults to .
+ ///
+ ///
+ ///
+ protected virtual void ColorSelection (List line, int idx)
{
Driver.SetAttribute (ColorScheme.Focus);
}
- void ColorUsed ()
+ ///
+ /// Sets the to an appropriate color for rendering the given of the
+ /// current . Override to provide custom coloring by calling
+ /// Defaults to .
+ ///
+ ///
+ ///
+ protected virtual void ColorUsed (List line, int idx)
{
Driver.SetAttribute (ColorScheme.HotFocus);
}
@@ -1667,12 +1696,12 @@ namespace Terminal.Gui {
var rune = idxCol >= lineRuneCount ? ' ' : line [idxCol];
var cols = Rune.ColumnWidth (rune);
if (idxCol < line.Count && selecting && PointInSelection (idxCol, idxRow)) {
- ColorSelection ();
+ ColorSelection (line, idxCol);
} else if (idxCol == currentColumn && idxRow == currentRow && !selecting && !Used
&& HasFocus && idxCol < lineRuneCount) {
- ColorUsed ();
+ ColorUsed (line, idxCol);
} else {
- ColorNormal ();
+ ColorNormal (line,idxCol);
}
if (rune == '\t' && TabWidth > 0) {
diff --git a/UICatalog/Scenarios/SyntaxHighlighting.cs b/UICatalog/Scenarios/SyntaxHighlighting.cs
new file mode 100644
index 000000000..249423c00
--- /dev/null
+++ b/UICatalog/Scenarios/SyntaxHighlighting.cs
@@ -0,0 +1,180 @@
+
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Text;
+using System.Text.RegularExpressions;
+using System.Threading.Tasks;
+using Terminal.Gui;
+using static UICatalog.Scenario;
+using Attribute = Terminal.Gui.Attribute;
+
+namespace UICatalog.Scenarios {
+ [ScenarioMetadata (Name: "Syntax Highlighting", Description: "Text editor with keyword highlighting")]
+ [ScenarioCategory ("Controls")]
+ class SyntaxHighlighting : Scenario {
+
+ public override void Setup ()
+ {
+ Win.Title = this.GetName ();
+ Win.Y = 1; // menu
+ Win.Height = Dim.Fill (1); // status bar
+ Top.LayoutSubviews ();
+
+ var menu = new MenuBar (new MenuBarItem [] {
+ new MenuBarItem ("_File", new MenuItem [] {
+ new MenuItem ("_Quit", "", () => Quit()),
+ })
+ });
+ Top.Add (menu);
+
+ var textView = new SqlTextView () {
+ X = 0,
+ Y = 0,
+ Width = Dim.Fill (),
+ Height = Dim.Fill (1),
+ };
+
+ textView.Init();
+
+ textView.Text = "SELECT TOP 100 * \nfrom\n MyDb.dbo.Biochemistry;";
+
+ Win.Add (textView);
+
+ var statusBar = new StatusBar (new StatusItem [] {
+ new StatusItem(Key.CtrlMask | Key.Q, "~^Q~ Quit", () => Quit()),
+
+ });
+
+
+ Top.Add (statusBar);
+ }
+
+
+ private void Quit ()
+ {
+ Application.RequestStop ();
+ }
+
+ private class SqlTextView : TextView{
+
+ private HashSet keywords = new HashSet(StringComparer.CurrentCultureIgnoreCase);
+ private Attribute blue;
+ private Attribute white;
+ private Attribute magenta;
+
+
+ public void Init()
+ {
+ keywords.Add("select");
+ keywords.Add("distinct");
+ keywords.Add("top");
+ keywords.Add("from");
+ keywords.Add ("create");
+ keywords.Add ("primary");
+ keywords.Add ("key");
+ keywords.Add ("insert");
+ keywords.Add ("alter");
+ keywords.Add ("add");
+ keywords.Add ("update");
+ keywords.Add ("set");
+ keywords.Add ("delete");
+ keywords.Add ("truncate");
+ keywords.Add ("as");
+ keywords.Add ("order");
+ keywords.Add ("by");
+ keywords.Add ("asc");
+ keywords.Add ("desc");
+ keywords.Add ("between");
+ keywords.Add ("where");
+ keywords.Add ("and");
+ keywords.Add ("or");
+ keywords.Add ("not");
+ keywords.Add ("limit");
+ keywords.Add ("null");
+ keywords.Add ("is");
+ keywords.Add ("drop");
+ keywords.Add ("database");
+ keywords.Add ("column");
+ keywords.Add ("table");
+ keywords.Add ("having");
+ keywords.Add ("in");
+ keywords.Add ("join");
+ keywords.Add ("on");
+ keywords.Add ("union");
+ keywords.Add ("exists");
+
+ magenta = Driver.MakeAttribute (Color.Magenta, Color.Black);
+ blue = Driver.MakeAttribute (Color.Cyan, Color.Black);
+ white = Driver.MakeAttribute (Color.White, Color.Black);
+ }
+
+ protected override void ColorNormal ()
+ {
+ Driver.SetAttribute (white);
+ }
+
+ protected override void ColorNormal (List line, int idx)
+ {
+ if(IsInStringLiteral(line,idx)) {
+ Driver.SetAttribute (magenta);
+ }
+ else
+ if(IsKeyword(line,idx))
+ {
+ Driver.SetAttribute (blue);
+ }
+ else{
+ Driver.SetAttribute (white);
+ }
+ }
+
+ private bool IsInStringLiteral (List line, int idx)
+ {
+ string strLine = new string (line.Select (r => (char)r).ToArray ());
+
+ foreach(Match m in Regex.Matches(strLine, "'[^']*'")) {
+ if(idx >= m.Index && idx < m.Index+m.Length) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ private bool IsKeyword(List line, int idx)
+ {
+ var word = IdxToWord(line,idx);
+
+ if(string.IsNullOrWhiteSpace(word)){
+ return false;
+ }
+
+ return keywords.Contains(word,StringComparer.CurrentCultureIgnoreCase);
+ }
+
+ private string IdxToWord(List line, int idx)
+ {
+ var words = Regex.Split(
+ new string(line.Select(r=>(char)r).ToArray()),
+ "\\b");
+
+
+ int count = 0;
+ string current = null;
+
+ foreach(var word in words)
+ {
+ current = word;
+ count+= word.Length;
+ if(count > idx){
+ break;
+ }
+ }
+
+ return current?.Trim();
+ }
+ }
+ }
+}