【问题标题】:AvalonEdit FoldingStrategy by Indent (Python)AvalonEdit FoldingStrategy by Indent (Python)
【发布时间】:2017-11-10 13:37:23
【问题描述】:

有人尝试过按缩进级别创建 FoldingStrategy 吗?就像编程语言 python。

BraceFoldingStrategy 没有问题,因为您有固定的开始和结束标记。有人想为制表符缩进创建这个吗?

【问题讨论】:

  • 您是在寻找一个完整的例子,还是只是一个关于如何完成的解释?您有创建自定义折叠策略的经验吗?
  • 一个完整的例子会很棒。我从来没有自己制定过新的折叠策略,但我了解它是如何工作的。

标签: c# python avalonedit


【解决方案1】:

这是一个主要功能性的解决方案。我没有时间测试所有的可能性,但是我的一些 python 脚本我坐在那里(诚然它们的格式很好),它表现得很好。我在制作它时添加了一些注释,以便它在折叠时显示行内容。基本上只是缓冲起始行,然后将其与起始索引放在一个元组或其他东西的堆栈上。

我确信这段代码不能正确处理许多使用 cmets 的情况。这是你必须测试和调整的东西。该代码相对幼稚,因此您可能还想添加对关键字的检查,而不仅仅是“如果它有一个冒号,那就是一个新的折叠开始”逻辑。此外,由于我之前的折叠经验,它以更复杂的字符迭代方式完成。我尝试了其他“更简单”的方法,例如在线运行正则表达式检查,这可能非常非常慢。

最后,您可能想要实际使用误差偏移量,而不是像我所做的那样仅仅放弃。这应该很容易添加。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using ICSharpCode.AvalonEdit.Highlighting;
using ICSharpCode.AvalonEdit.Highlighting.Xshd;
using ICSharpCode.AvalonEdit.Rendering;
using ICSharpCode.AvalonEdit.Document;
using ICSharpCode.AvalonEdit.Folding;
using System.Text.RegularExpressions;

namespace Foo.languages
{
    public class TabFoldingStrategy : AbstractFoldingStrategy
    {
        // How many spaces == one tab
        private const int SpacesInTab = 4;

        /// <summary>
        /// Creates a new TabFoldingStrategy.
        /// </summary>
        public TabFoldingStrategy() {
        }

        /// <summary>
        /// Create <see cref="NewFolding"/>s for the specified document.
        /// </summary>
        public override IEnumerable<NewFolding> CreateNewFoldings(TextDocument document, out int firstErrorOffset)
        {
            firstErrorOffset = -1;
            return CreateNewFoldingsByLine(document);
        }

        /// <summary>
        /// Create <see cref="NewFolding"/>s for the specified document.
        /// </summary>
        public IEnumerable<NewFolding> CreateNewFoldingsByLine(ITextSource document)
        {
            List<NewFolding> newFoldings = new List<NewFolding>();

            if (document == null || (document as TextDocument).LineCount <= 1)
            {
                return newFoldings;
            }

            //Can keep track of offset ourself and from testing it seems to be accurate
            int offsetTracker = 0;

            // Keep track of start points since things nest
            Stack<int> startOffsets = new Stack<int>();

            StringBuilder lineBuffer = new StringBuilder();


            foreach (DocumentLine line in (document as TextDocument).Lines)
            {
                if (offsetTracker >= document.TextLength)
                {
                    break;
                }

                lineBuffer.Clear();


                // First task is to get the line and figure out the spacing in front of it
                int spaceCounter = 0;
                bool foundText = false;
                bool foundColon = false;
                //for (int i = 0; i < line.Length; i++)
                int i = 0;
                //TODO buffer the characters so you can have the line contents on the stack too for the folding name (display text)
                while (i < line.Length && !(foundText && foundColon))
                {
                    char c = document.GetCharAt(offsetTracker + i);

                    switch (c)
                    {
                        case ' ': // spaces count as one
                            if (!foundText) {
                                spaceCounter++;
                            }
                            break;
                        case '\t': // Tabs count as N
                            if (!foundText) {
                                spaceCounter += SpacesInTab;
                            }
                            break;
                        case ':': // Tabs count as N
                            foundColon = true;
                            break;
                        default: // anything else means we encountered not spaces or tabs, so keep making the line but stop counting
                            foundText = true;
                            break;
                    }
                    i++;
                }

                // before we continue, we need to make sure its a correct multiple
                int remainder = spaceCounter % SpacesInTab;
                if (remainder > 0)
                {
                    // Some tabbing isn't correct. ignore this line for folding purposes.
                    // This may break all foldings below that, but it's a complex problem to address.
                    continue;
                }

                // Now we need to figure out if this line is a new folding by checking its tabing
                // relative to the current stack count. Convert into virtual tabs and compare to stack level
                int numTabs = spaceCounter / SpacesInTab; // we know this will be an int because of the above check
                if (numTabs >= startOffsets.Count && foundText && foundColon)
                {
                    // we are starting a new folding
                    startOffsets.Push(offsetTracker);

                }
                else // numtabs < offsets
                {
                    // we know that this is the end of a folding. It could be the end of multiple foldings. So pop until it matches.
                    while (numTabs < startOffsets.Count)
                    {
                        int foldingStart = startOffsets.Pop();
                        NewFolding tempFolding = new NewFolding();
                        //tempFolding.Name = < could add logic here, possibly by tracking key words when starting the folding, to control what is shown when it's folded >
                        tempFolding.StartOffset = foldingStart;
                        tempFolding.EndOffset = offsetTracker - 2; 
                        newFoldings.Add(tempFolding);
                    }
                }


                // Increment tracker. Much faster than getting it from the line
                offsetTracker += line.TotalLength;
            }

            // Complete last foldings
            while (startOffsets.Count > 0)
            {
                int foldingStart = startOffsets.Pop();
                NewFolding tempFolding = new NewFolding();
                //tempFolding.Name = < could add logic here, possibly by tracking key words when starting the folding, to control what is shown when it's folded >
                tempFolding.StartOffset = foldingStart;
                tempFolding.EndOffset = offsetTracker;
                newFoldings.Add(tempFolding);
            }

            newFoldings.Sort((a, b) => (a.StartOffset.CompareTo(b.StartOffset)));
            return newFoldings;
        }

    }

}

【讨论】:

  • 非常感谢您的回答,但我猜没有必要查看每个字符。并且空格没有被python重新定义为选项卡还是我错了?为什么有必要检查 ':' ?也许我可以用这个改进我的解决方案。
【解决方案2】:

我认识到 AvalonEdit.TextDocument 也可以基于行使用。所以我能够创建自己的解决方案:

using System;
using System.Collections.Generic;
using ICSharpCode.AvalonEdit.Document;

namespace ICSharpCode.AvalonEdit.Folding
{
    /// <summary>
    /// Allows producing tab based foldings
    /// </summary>
    public class TabFoldingStrategy : AbstractFoldingStrategy
    {
        internal class TabIndent
        {
            public int IndentSize;
            public int LineStart;
            public int LineEnd;
            public int StartOffset => LineStart + IndentSize - 1;
            public int TextLength => LineEnd - StartOffset;

            public TabIndent(int i_indentSize, int i_lineStart, int i_lineEnd)
            {
                IndentSize = i_indentSize;
                LineStart = i_lineStart;
                LineEnd = i_lineEnd;
            }
        }

        /// <summary>
        /// Creates a new TabFoldingStrategy.
        /// </summary>
        public TabFoldingStrategy()
        {

        }

        /// <summary>
        /// Create <see cref="NewFolding"/>s for the specified document.
        /// </summary>
        public override IEnumerable<NewFolding> CreateNewFoldings(TextDocument document, out int firstErrorOffset)
        {
            firstErrorOffset = -1;
            return CreateNewFoldings(document);
        }

        /// <summary>
        /// Create <see cref="NewFolding"/>s for the specified document.
        /// </summary>
        public IEnumerable<NewFolding> CreateNewFoldings(TextDocument document)
        {
            List<NewFolding> newFoldings = new List<NewFolding>();

            int documentIndent = 0;
            List<TabIndent> tabIndents = new List<TabIndent>();
            foreach (DocumentLine line in document.Lines) {
                int lineIndent = 0;
                for (int i = line.Offset; i < line.EndOffset; i++) {
                    char c = document.GetCharAt(i);
                    if (c == '\t') {
                        lineIndent++;
                    } else {
                        break;
                    }
                }
                if (lineIndent > documentIndent) {
                    tabIndents.Add(new TabIndent(lineIndent, line.PreviousLine.Offset, line.PreviousLine.EndOffset));
                } else if (lineIndent < documentIndent) {
                    List<TabIndent> closedIndents = tabIndents.FindAll(x => x.IndentSize > lineIndent);
                    closedIndents.ForEach(x => {
                        newFoldings.Add(new NewFolding(x.StartOffset, line.PreviousLine.EndOffset) {
                            Name = document.GetText(x.StartOffset, x.TextLength)
                        });
                        tabIndents.Remove(x);
                    });
                }
                documentIndent = lineIndent;
            }
            tabIndents.ForEach(x => {
                newFoldings.Add(new NewFolding(x.StartOffset, document.TextLength));
            });

            newFoldings.Sort((a, b) => a.StartOffset.CompareTo(b.StartOffset));
            return newFoldings;
        }
    }
}

更新:


我认为当有人将 python 与 AvalonEdit 一起使用时,这也非常有用。用于深色主题的语法高亮 xshd:

<?xml version="1.0"?>
<SyntaxDefinition name ="Python" extensions = ".py" xmlns="http://icsharpcode.net/sharpdevelop/syntaxdefinition/2008">

    <Color name="Comment" foreground="#808080" />        
    <Color name="String" foreground="#6A8759" />    
    <Color name="Keywords" foreground="#c75454" fontWeight="bold" />        
    <Color name="NumAndTypes" foreground="#21b0b0" />
    <Color name="FunctionCall" foreground="#38a1d4" />
    <Color name="Words" foreground="#8fb1ba" />

    <RuleSet>

        <Span color="Comment">
            <Begin>\#</Begin>
        </Span>

        <Span color="String" multiline="true">
            <Begin>'</Begin>
            <End>'</End>
        </Span>

        <Span color="String" multiline="true">
            <Begin>"</Begin>
            <End>"</End>
        </Span>

        <!-- Digits -->
        <Rule color="NumAndTypes">
            \b0[xX][0-9a-fA-F]+  # hex number
            |
            \b0[0-9]+ # octal number
            |
            (   \b\d+(\.[0-9]+)?   #number with optional floating point
            |   \.[0-9]+           #or just starting with floating point
            )
            ([eE][+-]?[0-9]+)? # optional exponent
        </Rule>

        <Keywords color="NumAndTypes">
            <Word>False</Word>
            <Word>True</Word>
            <Word>None</Word>
        </Keywords>


        <Keywords color="Keywords">
            <Word>class</Word>
            <Word>finally</Word>
            <Word>is</Word>
            <Word>return</Word>
            <Word>continue</Word>
            <Word>for</Word>
            <Word>lambda</Word>
            <Word>try</Word>
            <Word>def</Word>
            <Word>from</Word>
            <Word>nonlocal</Word>
            <Word>while</Word>
            <Word>and</Word>
            <Word>del</Word>
            <Word>global</Word>
            <Word>not</Word>
            <Word>with</Word>
            <Word>as</Word>
            <Word>elif</Word>
            <Word>if</Word>
            <Word>or</Word>
            <Word>yield</Word>
            <Word>assert</Word>
            <Word>else</Word>
            <Word>import</Word>
            <Word>pass</Word>
            <Word>break</Word>
            <Word>except</Word>
            <Word>in</Word>
            <Word>raise</Word>
        </Keywords>

        <Rule color="FunctionCall">
            \b
            [\d\w_]+  # an identifier
            (?=\s*\() # followed by (
        </Rule>

        <Rule color="Words">\w+</Rule>

    </RuleSet>

</SyntaxDefinition>

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-02-18
    • 1970-01-01
    • 2013-09-17
    • 1970-01-01
    • 2012-09-02
    • 2011-04-28
    相关资源
    最近更新 更多