RichTextBox中的代码折叠

3

我正在使用C#开发一个基于Winforms RichTextBox的代码编辑器。我已经实现了自动完成和语法高亮,但代码折叠需要采用不同的方法。我的目标是:

对于以下代码:

public static SomeFunction(EventArgs e)
{
    //Some code
    //Some code
    //Some code
    //Some code
    //Some code
    //Some code
}

Should become:

public static SomeFunction(EventArgs e)[...]

通过正则表达式或过程性代码,有什么想法或建议来实现这个功能呢?其中[...]是一个缩短的代码,在你悬停在[...]上时显示在工具提示中。


7
使用正则表达式很难使其工作。在注释或引用的字符串中放置一个杂乱的 {} 将导致任何正则表达式方案出现问题。最好使用Roslyn来对源代码进行词法分析和语法分析。 - Jim Mischel
2
解决方案似乎相当明显:您需要维护代码块的知识。您尝试过什么?天真的实现是使用一个栈,在非注释/字符串的 { 上推送,以 } 弹出。 - lukegravitt
嘿,lukegravitt,你能给我一些提示吗?如何做到呢? - devavx
1
http://www.codeproject.com/Articles/42490/Using-AvalonEdit-WPF-Text-Editor - I4V
它不会直接回答你的问题,但你可以画出自己的(或扩展别人的自定义绘制)文本编辑器。我会将文本分成行和跨度。其中跨度是类似文本的片段,例如保留关键字、定义类、开放括号、折叠代码块等。能够给你的跨度这样直接的属性将使你的项目在长期内更容易。 - Kind Contributor
1个回答

2

我已经创建了一个解析器,它将返回代码折叠位置的索引。

  • 折叠定界符由正则表达式定义。
  • 您可以指定起始和结束索引,以便在更新一个区域时不必检查整个代码。
  • 如果代码格式不正确,它会抛出异常,可以自由更改该行为。一种替代方法是它会一直向上移动堆栈,直到找到适当的结束标记。

折叠查找器

public class FoldFinder
{
    public static FoldFinder Instance { get; private set; }

    static FoldFinder()
    {
        Instance = new FoldFinder();
    }

    public List<SectionPosition> Find(string code, List<SectionDelimiter> delimiters, int start = 0,
        int end = -1)
    {
        List<SectionPosition> positions = new List<SectionPosition>();
        Stack<SectionStackItem> stack = new Stack<SectionStackItem>();

        int regexGroupIndex;
        bool isStartToken;
        SectionDelimiter matchedDelimiter;
        SectionStackItem currentItem;

        Regex scanner = RegexifyDelimiters(delimiters);

        foreach (Match match in scanner.Matches(code, start))
        {
            // the pattern for every group is that 0 corresponds to SectionDelimter, 1 corresponds to Start
            // and 2, corresponds to End.
            regexGroupIndex = 
                match.Groups.Cast<Group>().Select((g, i) => new {
                    Success = g.Success,
                    Index = i
                })
                .Where(r => r.Success && r.Index > 0).First().Index;

            matchedDelimiter = delimiters[(regexGroupIndex - 1) / 3];
            isStartToken = match.Groups[regexGroupIndex + 1].Success;

            if (isStartToken)
            {
                stack.Push(new SectionStackItem()
                {
                    Delimter = matchedDelimiter,
                    Position = new SectionPosition() { Start = match.Index }
                });
            }
            else
            {
                currentItem = stack.Pop();
                if (currentItem.Delimter == matchedDelimiter)
                {
                    currentItem.Position.End = match.Index + match.Length;
                    positions.Add(currentItem.Position);

                    // if searching for an end, and we've passed it, and the stack is empty then quit.
                    if (end > -1 && currentItem.Position.End >= end && stack.Count == 0) break;
                }
                else
                {
                    throw new Exception(string.Format("Invalid Ending Token at {0}", match.Index)); 
                }
            }
        }

        if (stack.Count > 0) throw new Exception("Not enough closing symbols.");

        return positions;
    }

    public Regex RegexifyDelimiters(List<SectionDelimiter> delimiters)
    {
        return new Regex(
            string.Join("|", delimiters.Select(d =>
                string.Format("(({0})|({1}))", d.Start, d.End))));
    }

}

public class SectionStackItem
{
    public SectionPosition Position;
    public SectionDelimiter Delimter;
}

public class SectionPosition
{
    public int Start;
    public int End;
}

public class SectionDelimiter
{
    public string Start;
    public string End;
}

示例查找

下面的示例匹配由{,}[,]和在符号右侧直到;的分隔符折叠。我没有看到太多IDE为每行折叠,但在长代码片段(如LINQ查询)中可能会很方便。

var sectionPositions = 
    FoldFinder.Instance.Find("abc { def { qrt; ghi [ abc ] } qrt }", new List<SectionDelimiter>(
        new SectionDelimiter[3] {
            new SectionDelimiter() { Start = "\\{", End = "\\}" },
            new SectionDelimiter() { Start = "\\[", End = "\\]" },
            new SectionDelimiter() { Start = "(?<=\\[|\\{|;|^)[^[{;]*(?=;)", End = ";" },
        }));

感谢您的简短描述,点赞 +1。 - devavx

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接