用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);
}