如何修复C# CLI编辑器中的重复单词及语法高亮异常问题
自研C# CLI编辑器的两类bug修复
问题说明
- 删除语法高亮单词时,编辑器界面会出现单词重复的视觉bug
- 仅当输入完整的语法高亮词时才会着色,输入过程中(未完成正确词输入)或输入错误拼写的词时,无任何高亮反馈
问题根源与修复方案
问题1:删除操作导致的单词重复
原因:原代码处理Backspace时,只是在控制台用空格覆盖字符,但后台文本中是用空格替换被删除的字符(而非真正删除),后续高亮逻辑仍会匹配到文本中残留的完整高亮词,重新绘制到控制台,造成视觉重叠。
修复:
- Backspace处理时直接从后台文本中删除对应位置的字符,而非替换为空格
- 删除后重绘当前行,彻底清除残留的视觉内容
问题2:输入过程中无实时高亮
原因:原代码仅匹配完整的高亮单词,且每次高亮时直接在原位置重写单词,未先清除该行原有内容,导致输入过程中看不到实时反馈。
修复:
- 新增行重绘方法,每次输入/删除操作后刷新当前行的全部内容
- 支持前缀匹配高亮,输入过程中(比如输入
<htm时)就能看到对应颜色反馈
修复后的完整代码
using System; using System.Collections.Generic; using System.IO; using System.Linq; namespace ConsoleApp { class Program { static bool isInEditorMode = false; static Dictionary<string, ConsoleColor> highlightColors = new Dictionary<string, ConsoleColor>(); static void Main(string[] args) { Console.Title = "SCRIPTER - Version 1.01"; highlightColors.Add("<html>", ConsoleColor.DarkBlue); highlightColors.Add("</html>", ConsoleColor.DarkBlue); highlightColors.Add("<head>", ConsoleColor.Blue); highlightColors.Add("</head>", ConsoleColor.Blue); highlightColors.Add("<title>", ConsoleColor.Red); highlightColors.Add("</title>", ConsoleColor.Red); highlightColors.Add("<body>", ConsoleColor.DarkGreen); highlightColors.Add("</body>", ConsoleColor.DarkGreen); highlightColors.Add("<table>", ConsoleColor.DarkRed); highlightColors.Add("</table>", ConsoleColor.DarkRed); highlightColors.Add("<marquee>", ConsoleColor.DarkYellow); highlightColors.Add("</marquee>", ConsoleColor.DarkYellow); Console.Clear(); string input; while (true) { Console.WriteLine( @"▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ██░▄▄▄░██░▄▄▀██░▄▄▀█▄░▄██░▄▄░█▄▄░▄▄██░▄▄▄██░▄▄▀███▀███▀█▀░█████░▄▄░█▀░██ ██▄▄▄▀▀██░█████░▀▀▄██░███░▀▀░███░████░▄▄▄██░▀▀▄████░▀░███░██▀▀█░▀▄░██░██ ██░▀▀▀░██░▀▀▄██░██░█▀░▀██░██████░████░▀▀▀██░██░█████▄███▀░▀█▄▄█░▀▀░█▀░▀█ ▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀" ); Console.WriteLine( @" .---------. | samTime | '---------' " ); Console.WriteLine("Enter E to enter editor mode, I to open a file, or Q to quit the program."); input = Console.ReadLine(); if (input.ToUpper() == "E") { isInEditorMode = true; EditorMode(); isInEditorMode = false; } else if (input.ToUpper() == "I") { Console.WriteLine("Enter the path of the file to open: "); string filePath = Console.ReadLine(); if (File.Exists(filePath)) { string text = File.ReadAllText(filePath); isInEditorMode = true; EditorMode(text); isInEditorMode = false; } else { Console.Clear(); Console.WriteLine($"{filePath} does not exist"); } } else if (input.ToUpper() == "Q") { break; } } Console.WriteLine("Press any key to exit..."); Console.ReadKey(); } static void EditorMode(string text = "") { Console.Clear(); Console.WriteLine("Editor mode. Press Ctrl+S to save, Ctrl+X to exit."); int topMargin = Console.CursorTop; int leftMargin = Console.CursorLeft; int currentLine = topMargin; int currentPosition = leftMargin; // 初始化绘制全部文本 RedrawAllText(text, topMargin, leftMargin); currentPosition = leftMargin + text.Split(new[] { Environment.NewLine }, StringSplitOptions.None).Last().Length; ConsoleKeyInfo keyInfo; do { keyInfo = Console.ReadKey(true); if (isInEditorMode) { if (keyInfo.Modifiers == ConsoleModifiers.Control && keyInfo.Key == ConsoleKey.S) { Console.Clear(); SaveFile(text); return; } else if (keyInfo.Key == ConsoleKey.Backspace && currentPosition > leftMargin) { var lines = text.Split(new[] { Environment.NewLine }, StringSplitOptions.None); if (currentLine - topMargin >= lines.Length) break; int startIndex = lines.Take(currentLine - topMargin).Sum(x => x.Length + Environment.NewLine.Length); int currentIndex = startIndex + (currentPosition - leftMargin); if (currentIndex > 0 && currentIndex <= text.Length) { // 真正删除字符 text = text.Remove(currentIndex - 1, 1); currentPosition--; // 重绘当前行 RedrawCurrentLine(text, currentLine, topMargin, leftMargin); } } else if (keyInfo.Modifiers == ConsoleModifiers.Control && keyInfo.Key == ConsoleKey.X) { Console.Clear(); return; } else if (keyInfo.Key == ConsoleKey.LeftArrow && currentPosition > leftMargin) { currentPosition--; Console.SetCursorPosition(currentPosition, currentLine); } else if (keyInfo.Key == ConsoleKey.RightArrow && currentPosition < leftMargin + GetCurrentLineLength(text, currentLine, topMargin)) { currentPosition++; Console.SetCursorPosition(currentPosition, currentLine); } else if (keyInfo.Key == ConsoleKey.UpArrow && currentLine > topMargin) { currentLine--; int lineLength = GetCurrentLineLength(text, currentLine, topMargin); currentPosition = Math.Min(leftMargin + lineLength, currentPosition); Console.SetCursorPosition(currentPosition, currentLine); } else if (keyInfo.Key == ConsoleKey.DownArrow && currentLine < topMargin + text.Split('\n').Length - 1) { currentLine++; int lineLength = GetCurrentLineLength(text, currentLine, topMargin); currentPosition = Math.Min(leftMargin + lineLength, currentPosition); Console.SetCursorPosition(currentPosition, currentLine); } else if (keyInfo.Key == ConsoleKey.Enter) { var lines = text.Split(new[] { Environment.NewLine }, StringSplitOptions.None); int startIndex = lines.Take(currentLine - topMargin).Sum(x => x.Length + Environment.NewLine.Length); int currentIndex = startIndex + (currentPosition - leftMargin); text = text.Insert(currentIndex, Environment.NewLine); currentLine++; currentPosition = leftMargin; RedrawAllText(text, topMargin, leftMargin); } else if (char.IsControl(keyInfo.KeyChar)) { // 忽略其他控制字符 } else { var lines = text.Split(new[] { Environment.NewLine }, StringSplitOptions.None); int startIndex = lines.Take(currentLine - topMargin).Sum(x => x.Length + Environment.NewLine.Length); int currentIndex = startIndex + (currentPosition - leftMargin); text = text.Insert(currentIndex, keyInfo.KeyChar.ToString()); currentPosition++; RedrawCurrentLine(text, currentLine, topMargin, leftMargin); } Console.SetCursorPosition(currentPosition, currentLine); } } while (true); } // 获取当前行的长度 static int GetCurrentLineLength(string text, int currentLine, int topMargin) { var lines = text.Split(new[] { Environment.NewLine }, StringSplitOptions.None); int lineIndex = currentLine - topMargin; if (lineIndex < 0 || lineIndex >= lines.Length) return 0; return lines[lineIndex].Length; } // 重绘当前行 static void RedrawCurrentLine(string text, int currentLine, int topMargin, int leftMargin) { var lines = text.Split(new[] { Environment.NewLine }, StringSplitOptions.None); int lineIndex = currentLine - topMargin; if (lineIndex < 0 || lineIndex >= lines.Length) return; // 清除当前行 Console.SetCursorPosition(leftMargin, currentLine); Console.Write(new string(' ', Console.WindowWidth - leftMargin)); // 写入当前行文本 string lineText = lines[lineIndex]; Console.SetCursorPosition(leftMargin, currentLine); Console.Write(lineText); // 应用高亮 ApplyHighlightToLine(lineText, currentLine, leftMargin); } // 重绘全部文本 static void RedrawAllText(string text, int topMargin, int leftMargin) { Console.SetCursorPosition(leftMargin, topMargin); // 清除编辑器区域 for (int i = topMargin; i < Console.WindowHeight; i++) { Console.SetCursorPosition(leftMargin, i); Console.Write(new string(' ', Console.WindowWidth - leftMargin)); } // 写入全部文本 Console.SetCursorPosition(leftMargin, topMargin); Console.Write(text); // 应用所有高亮 var lines = text.Split(new[] { Environment.NewLine }, StringSplitOptions.None); for (int i = 0; i < lines.Length; i++) { ApplyHighlightToLine(lines[i], topMargin + i, leftMargin); } } // 对单行应用高亮(支持前缀匹配) static void ApplyHighlightToLine(string lineText, int line, int leftMargin) { foreach (var highlight in highlightColors) { string targetWord = highlight.Key; ConsoleColor color = highlight.Value; // 匹配完整单词 int index = lineText.IndexOf(targetWord); while (index >= 0) { Console.SetCursorPosition(leftMargin + index, line); Console.ForegroundColor = color; Console.Write(targetWord); Console.ResetColor(); index = lineText.IndexOf(targetWord, index + targetWord.Length); } // 匹配输入过程中的前缀 for (int len = 1; len < targetWord.Length; len++) { string prefix = targetWord.Substring(0, len); int prefixIndex = lineText.IndexOf(prefix); while (prefixIndex >= 0) { if (prefixIndex + len > lineText.Length || lineText[prefixIndex + len] != targetWord[len]) { Console.SetCursorPosition(leftMargin + prefixIndex, line); Console.ForegroundColor = color; Console.Write(prefix); Console.ResetColor(); } prefixIndex = lineText.IndexOf(prefix, prefixIndex + len); } } } } static void SaveFile(string text) { Console.Clear(); Console.WriteLine("Enter file name with extension: "); string fileName = Console.ReadLine(); File.WriteAllText(fileName, text); Console.Clear(); Console.WriteLine($"File saved as {fileName}."); Console.WriteLine("Do you want to open the file? (Y/N)"); string input = Console.ReadLine(); if (input.ToUpper() == "Y") { System.Diagnostics.Process.Start(new System.Diagnostics.ProcessStartInfo(fileName) { UseShellExecute = true }); Console.Clear(); } else if (input.ToUpper() == "N") { Console.Clear(); } } } }
修复说明
- 删除bug修复:Backspace操作直接移除文本中的字符,同时重绘当前行,彻底解决视觉重复问题
- 实时高亮优化:通过重绘机制保证界面内容与后台文本一致,新增前缀匹配逻辑,输入过程中就能看到对应颜色反馈,提升编辑流畅度
内容的提问来源于stack exchange,提问作者Sam8848
相关产品推荐
相关产品推荐

