You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何修复C# CLI编辑器中的重复单词及语法高亮异常问题

自研C# CLI编辑器的两类bug修复

问题说明

  1. 删除语法高亮单词时,编辑器界面会出现单词重复的视觉bug
  2. 仅当输入完整的语法高亮词时才会着色,输入过程中(未完成正确词输入)或输入错误拼写的词时,无任何高亮反馈

问题根源与修复方案

问题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();
            }
        }
    }
}

修复说明

  1. 删除bug修复:Backspace操作直接移除文本中的字符,同时重绘当前行,彻底解决视觉重复问题
  2. 实时高亮优化:通过重绘机制保证界面内容与后台文本一致,新增前缀匹配逻辑,输入过程中就能看到对应颜色反馈,提升编辑流畅度

内容的提问来源于stack exchange,提问作者Sam8848

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.19 08:14:53