使用自定义 Lexer (ScintillaNET) 进行代码折叠

问题描述 投票:0回答:1

问题是 Scintilla 控件会自动进行折叠,这意味着如果您使用自己的词法分析器,折叠将不起作用。

如果你想看一下代码,我正在使用它来配置控件:

https://github.com/robinrodricks/ScintillaNET.Demo

这解释了如何配置代码文件夹:

https://github.com/jacobslusser/ScintillaNET/wiki/Automatic-Code-Folding

也就是说,我已经制作了自己的词法分析器,这意味着代码文件夹不起作用。有谁知道如何使其与自定义词法分析器一起使用?

我只想在

{
}
时折叠代码。

(.NET 7 Winforms C#)

c# .net lexer folding scintilla
1个回答
0
投票

你问了一个很好的问题,目前可用的信息很少。此外,您提供的链接正是寻找如何实现这一点的线索的地方。

我还建议以下内容,讨论“容器”词法分析器的折叠实现:https://github.com/LuaDist/scintilla/blob/master/doc/Lexer.txt

另一个关键来源是 ScintillaNET 源代码 https://github.com/jacobslusser/ScintillaNET,它提供了干净地实现代码折叠所需的许多变量声明。

我建议类似以下内容:

public class FoldTextControl : ScintillaNET.Scintilla
{
    public FoldTextControl() : base()
    {
        Lexer = Lexer.Container;
        SetupFolding();
    }

    private void SetupFolding()
    {
        const int FOLD_LEVEL_BASE = 0x400; // Copied from sealed ScintillaNET.NativeMethods class
        const int FOLD_MARGIN = 2;

        int[] FolderIndices = new int[]
        {
            Marker.Folder,
            Marker.FolderEnd,
            Marker.FolderMidTail,
            Marker.FolderOpen,
            Marker.FolderOpenMid,
            Marker.FolderSub,
            Marker.FolderTail,
        }

        Margins[FOLD_MARGIN].Type = MarginType.Symbol;
        Margins[FOLD_MARGIN].Mask = Marker.MaskFolders;
        Margins[FOLD_MARGIN].Sensitive = true;
        Margins[FOLD_MARGIN].Width = 20;

        foreach(int index in FolderIndices)
        {
            Markers[index].SetForeColor(SystemColors.ControlLightLight);
            Markers[index].SetBackColor(SystemColors.ControlDark);
        }

        Markers[Marker.Folder].Symbol = MarkerSymbol.BoxPlus;
        Markers[Marker.FolderOpen].Symbol = MarkerSymbol.BoxMinus;
        Markers[Marker.FolderEnd].Symbol = MarkerSymbol.BoxPlusConnected;
        Markers[Marker.FolderMidTail].Symbol = MarkerSymbol.TCorner;
        Markers[Marker.FolderOpenMid].Symbol = MarkerSymbol.BoxMinusConnected;
        Markers[Marker.FolderSub].Symbol = MarkerSymbol.VLine;
        Markers[Marker.FolderTail].Symbol = MarkerSymbol.LCorner;

        AutomaticFold = AutomaticFold.Show | AutomaticFold.Click | AutomaticFold.Change;

        StyleNeeded += (sender, e) =>
        {
            for(int index = 0; index < Lines.Count; index++)
            {
                string lineText = Lines[index].Text;
                Lines[index].FoldLevel = FOLD_LEVEL_BASE;
                /*
                    Your custom code goes here to implement folding by doing the following:

                        Lines[index].FoldLevel += [Add a score for where this line fits in the heirachy of line folding]

                        if(lineText.Trim() == "")
                            Lines[index].FoldLevelFlags = FoldLevel.White; // Line is whitespace within the fold heirachy

                        if([Line should be a fold point])
                            Lines[index].FoldLevelFlags = FoldLevel.Header;
                */
            }
        };
    }
}

有文档表明需要 SetProperty("fold", "1")SetProperty("fold.compact", "1") 。如果没有这个,它似乎工作得很好,所以我想知道这个建议是否特定于非“容器”类型的词法分析器。

如果您需要显示正确的折叠标记,则从 FOLD_LEVEL_BASE 开始设置折叠级别至关重要。

对于 ScintillaNET 行折叠的最小可行示例,这是我用于基于空白的行折叠的示例。

StyleNeeded += (sender, e) =>
{
    for(int index = 0; index < Lines.Count; index++)
    {
        string lineText = Lines[index].Text;
        Lines[index].FoldLevel = FOLD_LEVEL_BASE;
        if(lineText.Trim() == "" && !lineText.Contains(' '))
            continue;
        int indentLevel = lineText.TakeWhile(char.IsWhiteSpace).Count();
        Lines[index].FoldLevel += indentLevel;
        if(lineText.Trim() == "")
            Lines[index].FoldLevelFlags = FoldLevelFlags.White;
        else
            Lines[index].FoldLevelFlags = FoldLevelFlags.Header;
    }
};

根据您的要求,您可能需要如下内容:

StyleNeeded += (sender, e) =>
{
    int indentLevel = 0;
    for(int index = 0; index < Lines.Count; index++)
    {
        Lines[index].FoldLevel = FOLD_LEVEL_BASE + indentLevel;
        string lineText = Lines[index].Text;
        if(lineText.Trim() == "")
        {
            Lines[index].FoldLevelFlags = FoldLevelFlags.White;
            continue;
        }
        foreach(char c in line)
        {
            if(c == '{')
            {
                indentLevel++;
                Lines[index].FoldLevelFlags = FoldLevelFlags.Header;
            }
            else if(c == '}')
            {
                indentLevel--;
            }
        }

    }
};
© www.soinside.com 2019 - 2024. All rights reserved.