WPF中扩展AvalonEdit以支持Python代码折叠功能

用AI写的,花了一整天,用过Kimi、deepseek、豆包、千问,最终豆包成果被采用,贴出源码:

using ICSharpCode.AvalonEdit.Document;

namespace ICSharpCode.AvalonEdit.Folding
{
    /// <summary>
    /// Holds information about the start of a fold in a Python string.
    /// </summary>
    sealed class PythonFoldStart : NewFolding
    {
        internal int StartLine;
        internal int Indent;
    }

    /// <summary>
    /// Determines folds for a Python string in the editor.
    /// </summary>
    public class PythonFoldingStrategy
    {
        /// <summary>
        /// Create <see cref="NewFolding"/>s for the specified document and updates the folding manager with them.
        /// </summary>
        public void UpdateFoldings(FoldingManager manager, TextDocument document)
        {
            int firstErrorOffset;
            IEnumerable<NewFolding> foldings = CreateNewFoldings(document, out firstErrorOffset);
            manager.UpdateFoldings(foldings, firstErrorOffset);
        }

        /// <summary>
        /// Create <see cref="NewFolding"/>s for the specified document.
        /// </summary>
        public IEnumerable<NewFolding> CreateNewFoldings(TextDocument document, out int firstErrorOffset)
        {
            try
            {
                Stack<PythonFoldStart> stack = new Stack<PythonFoldStart>();
                List<NewFolding> foldMarkers = new List<NewFolding>();
                firstErrorOffset = -1;

                for (int lineNumber = 1; lineNumber <= document.LineCount; lineNumber++)
                {
                    DocumentLine line = document.GetLineByNumber(lineNumber);
                    string lineText = document.GetText(line);

                    if (string.IsNullOrEmpty(lineText.Trim()))
                        continue;

                    int indent = GetIndentation(lineText);

                    // 处理缩进减少的情况,即代码块结束
                    while (stack.Count > 0 && stack.Peek().Indent >= indent)
                    {
                        PythonFoldStart foldStart = stack.Pop();
                        CreatePythonFold(document, foldMarkers, lineNumber, foldStart);
                    }

                    // 检查是否为新的代码块开始
                    if (lineText.TrimEnd().EndsWith(":"))
                    {
                        PythonFoldStart newFoldStart = CreatePythonFoldStart(document, lineNumber, indent, lineText);
                        stack.Push(newFoldStart);
                    }
                }

                // 处理栈中剩余的代码块
                while (stack.Count > 0)
                {
                    PythonFoldStart foldStart = stack.Pop();
                    CreatePythonFold(document, foldMarkers, document.LineCount + 1, foldStart);
                }

                foldMarkers.Sort((a, b) => a.StartOffset.CompareTo(b.StartOffset));
                return foldMarkers;
            }
            catch (Exception)
            {
                firstErrorOffset = 0;
                return Enumerable.Empty<NewFolding>();
            }
        }

        static int GetIndentation(string line)
        {
            int indent = 0;
            foreach (char c in line)
            {
                if (char.IsWhiteSpace(c))
                    indent++;
                else
                    break;
            }
            return indent;
        }

        /// <summary>
        /// Creates a PythonFoldStart for the start of a block.
        /// </summary>
        PythonFoldStart CreatePythonFoldStart(TextDocument document, int lineNumber, int indent, string lineText)
        {
            PythonFoldStart newFoldStart = new PythonFoldStart();
            newFoldStart.StartLine = lineNumber;
            newFoldStart.StartOffset = document.GetLineByNumber(lineNumber).Offset;
            newFoldStart.Name = $"{lineText}...";
            newFoldStart.Indent = indent;
            return newFoldStart;
        }

        /// <summary>
        /// Create a Python fold if the start and end of the block are on different lines.
        /// </summary>
        static void CreatePythonFold(TextDocument document, List<NewFolding> foldMarkers, int endLineNumber, PythonFoldStart foldStart)
        {
            if (endLineNumber > foldStart.StartLine)
            {
                DocumentLine endLine = document.GetLineByNumber(endLineNumber - 1);
                foldStart.EndOffset = endLine.EndOffset;
                foldMarkers.Add(foldStart);
            }
        }
    }
}

使用方法:
PythonCodeView.xaml中添加avalonEdit组件

<Window x:Class="XXX.App.Views.GraphicalCode.PythonCodeView"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:PMS.App.Views.GraphicalCode"
        xmlns:avalonEdit="http://icsharpcode.net/sharpdevelop/avalonedit" 
        xmlns:shell="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        mc:Ignorable="d" WindowStyle="None" ShowInTaskbar="False"
        d:Height="800" d:Width="500" IsVisibleChanged="Window_IsVisibleChanged">
    <shell:WindowChrome.WindowChrome>
        <shell:WindowChrome 
            CaptionHeight="0"
            ResizeBorderThickness="0"
            GlassFrameThickness="0"
            CornerRadius="0"
            UseAeroCaptionButtons="False"/>
    </shell:WindowChrome.WindowChrome>
    
    <Grid>
        <avalonEdit:TextEditor
            Name="PythonEditor" Opacity="1"
            ShowLineNumbers="True"
            SyntaxHighlighting="Python" 
            FontFamily="Consolas"
            FontSize="14"
            Background="#001526"
            Foreground="White"
            WordWrap ="True"
            Focusable="True">
            <avalonEdit:TextEditor.Options>
                <avalonEdit:TextEditorOptions 
                    ShowSpaces="True" 
                    WordWrapIndentation="4" 
                    InheritWordWrapIndentation="True"/>
            </avalonEdit:TextEditor.Options>
        </avalonEdit:TextEditor>
    </Grid>
</Window>

PythonCodeView.xaml.cs代码片段:

private TextDocument document = new TextDocument();
private FoldingManager foldingManager;
private PythonFoldingStrategy foldingStrategy;

public PythonCodeView()
{
    //do other somethings...
    PythonEditor.Document = document;
    document.TextChanged += Document_Changed;
    foldingManager = FoldingManager.Install(PythonEditor.TextArea);
    foldingStrategy = new PythonFoldingStrategy();
}

private void Document_Changed(object? sender, EventArgs e)
{
    foldingStrategy.UpdateFoldings(foldingManager, document);
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值