添加项目文件。

This commit is contained in:
ShrlAlgo
2025-09-16 16:06:41 +08:00
parent 0e7807b826
commit 98c65ceb3d
922 changed files with 1009489 additions and 0 deletions

View File

@@ -0,0 +1,137 @@
using System.Collections.Generic;
using ICSharpCode.AvalonEdit.Document;
using ICSharpCode.AvalonEdit.Folding;
namespace ScriptPad.Editor
{
public abstract class FoldingStrategy
{
public string startToken { get; private set; }
public string endToken { get; private set; }
public FoldingStrategy(string start, string end)
{
this.startToken = start;
this.endToken = end;
}
public virtual void SetName(TextDocument document, NewFolding folding)
{
return;
}
public virtual void OnMatchStart(TextDocument textDocument, int index)
{
return;
}
}
public class BraceFoldingStrategy : FoldingStrategy
{
public BraceFoldingStrategy() : base("{", "}")
{
}
}
public class RegionFoldingStrategy : FoldingStrategy
{
public RegionFoldingStrategy() : base("#region", "#endregion")
{
}
}
public class CSharpFoldingStrategy
{
private List<FoldingStrategy> foldingStrategies;
public CSharpFoldingStrategy()
{
foldingStrategies = new List<FoldingStrategy>()
{
new BraceFoldingStrategy(),
new RegionFoldingStrategy()
};
}
public void UpdateFoldings(FoldingManager manager, TextDocument document)
{
int firstErrorOffset;
IEnumerable<NewFolding> newFoldings = CreateNewFoldings(document, out firstErrorOffset);
manager.UpdateFoldings(newFoldings, firstErrorOffset);
}
/// <summary>
/// Create <see cref="NewFolding"/>s for the specified document.
/// </summary>
public 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(ITextSource document)
{
List<NewFolding> newFoldings = new List<NewFolding>();
Stack<int> startOffsets = new Stack<int>();
int lastNewLineOffset = 0;
for (int i = 0; i < document.TextLength; i++)
{
var c = document.GetCharAt(i);
if (Is(document.Text, i, "\r\n"))
{
lastNewLineOffset++;
continue;
}
foreach (var item in foldingStrategies)
{
if (Is(document.Text, i, item.startToken))
{
startOffsets.Push(i);
i += item.endToken.Length;
}
else if (Is(document.Text, i, item.endToken) && startOffsets.Count > 0)
{
var startOffset = startOffsets.Pop();
if (startOffset < i)
{
var folding = new NewFolding(startOffset, i + 1);
SetName(folding);
newFoldings.Add(folding);
}
i += item.endToken.Length;
}
}
}
newFoldings.Sort((a, b) => a.StartOffset.CompareTo(b.StartOffset));
return newFoldings;
}
private bool Is(string source, int index, string str)
{
if (index + str.Length > source.Length)
{
return false;
}
for (var i = 0; i < str.Length; i++)
{
if (source[index + i] != str[i])
{
return false;
}
}
return true;
}
protected virtual void SetName(NewFolding folding)
{
return;
}
}
}

View File

@@ -0,0 +1,52 @@
using ICSharpCode.AvalonEdit.Document;
using System;
namespace ScriptPad
{
class CSIndentationStrategy : ICSharpCode.AvalonEdit.Indentation.IIndentationStrategy
{
string IndentationString = " ";
public void IndentLine(ICSharpCode.AvalonEdit.Document.TextDocument document, DocumentLine line)
{
if (document == null)
throw new ArgumentNullException("document");
if (line == null)
throw new ArgumentNullException("line");
DocumentLine previousLine = line.PreviousLine;
if (previousLine != null)
{
ISegment indentationSegment = TextUtilities.GetWhitespaceAfter(document, previousLine.Offset);
string indentation = document.GetText(indentationSegment);
if (previousLine.EndOffset > 0)
{
var c = document.GetCharAt(previousLine.EndOffset - 1);
if (c == '{')
{
//indentation += IndentationString;
indentationSegment = TextUtilities.GetWhitespaceAfter(document, line.Offset);
document.Replace(indentationSegment.Offset, indentationSegment.Length, indentation, OffsetChangeMappingType.RemoveAndInsert);
}
else
{
indentationSegment = TextUtilities.GetWhitespaceAfter(document, line.Offset);
document.Replace(indentationSegment.Offset, indentationSegment.Length, indentation, OffsetChangeMappingType.RemoveAndInsert);
}
}
}
}
public void IndentLines(ICSharpCode.AvalonEdit.Document.TextDocument document, int beginLine, int endLine)
{
}
}
}

View File

@@ -0,0 +1,44 @@
using ICSharpCode.AvalonEdit.CodeCompletion;
using ICSharpCode.AvalonEdit.Document;
using System;
using System.Collections.Immutable;
using System.Threading.Tasks;
using System.Windows;
using Microsoft.CodeAnalysis;
using System.Windows.Media;
using ICSharpCode.AvalonEdit.Editing;
using Microsoft.CodeAnalysis.Tags;
using System.Linq;
namespace ScriptPad.Editor
{
public class CodeCompletionData : ICompletionData
{
public CodeCompletionData(DocumentId id, Microsoft.CodeAnalysis.Completion.CompletionItem item)
{
this.Text = item.DisplayText;
image = new Lazy<ImageSource>(() => ImageResource.GetImage(item.Tags));
description = new Lazy<object>(() => Service.ScriptCompletionService.GetDescriptionAsync(id, item).Result);
this.Content = Text;
}
public string Text { get; }
public object Description { get => description.Value; }
public object Content { get; set; }
public ImageSource Image { get => image.Value; }
public double Priority => 0;
public void Complete(TextArea textArea, ISegment completionSegment, EventArgs insertionRequestEventArgs)
{
textArea.Document.Replace(completionSegment, Text);
}
private Lazy<object> description;
private Lazy<ImageSource> image;
}
}

View File

@@ -0,0 +1,29 @@
using System.Reactive.Linq;
using System;
using ICSharpCode.AvalonEdit.Editing;
using System.Windows.Input;
using ICSharpCode.AvalonEdit;
namespace ScriptPad.Editor
{
static class CodeEditorReactiveExtensions
{
public static IObservable<TextCompositionEventArgs> GetTextEnterings(this TextArea textArea)
{
return Observable.FromEventPattern<TextCompositionEventArgs>(textArea, nameof(textArea.TextEntering))
.Select(p => p.EventArgs);
}
public static IObservable<TextCompositionEventArgs> GetTextEntereds(this TextArea textArea)
{
return Observable.FromEventPattern<TextCompositionEventArgs>(textArea, nameof(textArea.TextEntered))
.Select(p => p.EventArgs);
}
public static IObservable<EventArgs> GetTextChangeds(this TextEditor editor)
{
return Observable.FromEventPattern<EventArgs>(editor, nameof(editor.TextChanged))
.Select(p => p.EventArgs);
}
}
}

View File

@@ -0,0 +1,42 @@
using System;
using ICSharpCode.AvalonEdit.Document;
using Microsoft.CodeAnalysis.Text;
namespace ScriptPad.Editor
{
public class TextContainer : SourceTextContainer , IDisposable
{
public TextContainer(TextDocument document)
{
this.TextDocument = document;
this.currentText = SourceText.From(document.GetText(0, document.TextLength));
document.Changed += Document_Changed;
}
private void Document_Changed(object sender, DocumentChangeEventArgs e)
{
var old = currentText;
var remove = new TextChange(new TextSpan(e.Offset, e.RemovalLength), "");
currentText = currentText.WithChanges(remove);
var insert = new TextChange(new TextSpan(e.Offset, 0), e.InsertedText.Text);
currentText = currentText.WithChanges(insert);
TextChanged?.Invoke(this, new Microsoft.CodeAnalysis.Text.TextChangeEventArgs(old, currentText, remove, insert));
}
public TextDocument TextDocument { get; private set; }
private SourceText currentText;
public override SourceText CurrentText => currentText;
public override event EventHandler<Microsoft.CodeAnalysis.Text.TextChangeEventArgs> TextChanged;
public void Dispose()
{
this.TextDocument.Changed -= Document_Changed;
}
}
}

View File

@@ -0,0 +1,20 @@
using ICSharpCode.AvalonEdit.Document;
using System.Windows.Media;
namespace ScriptPad.Editor
{
public class TextMarker : TextSegment
{
public TextMarker(int startOffset, int length, string message, Color markerColor)
{
StartOffset = startOffset;
Length = length;
Message = message;
MarkerColor = markerColor;
}
public string Message { get; }
public Color MarkerColor { get; }
}
}

View File

@@ -0,0 +1,196 @@
using ICSharpCode.AvalonEdit;
using ICSharpCode.AvalonEdit.Document;
using ICSharpCode.AvalonEdit.Rendering;
using Microsoft.CodeAnalysis;
using ScriptPad.Roslyn;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Threading;
namespace ScriptPad.Editor
{
public class TextMarkerService : IBackgroundRenderer
{
private readonly TextEditor textEditor;
public CsScript Script { get; set; }
private readonly TextSegmentCollection<TextMarker> markers;
private ToolTip toolTip;
DispatcherTimer Timer = new DispatcherTimer();
public TextMarkerService(TextEditor textEditor, CsScript script)
{
this.textEditor = textEditor;
this.Script = script;
this.markers = new TextSegmentCollection<TextMarker>(textEditor.Document);
TextView textView = textEditor.TextArea.TextView;
textView.BackgroundRenderers.Add(this);
textView.MouseHover += TextViewMouseHover;
textView.MouseHoverStopped += TextViewMouseHoverStopped;
textView.VisualLinesChanged += TextViewVisualLinesChanged;
Timer.Interval = TimeSpan.FromSeconds(2);
Timer.Tick += Timer_Tick;
Timer.Start();
}
private async void Timer_Tick(object sender, EventArgs e)
{
var document = textEditor.Document;
Clear();
var diagnostics = await Script.GetDiagnostics();
var listItems = diagnostics
.Where(x => x.Severity != DiagnosticSeverity.Hidden)
.Select(ErrorListItem.CreateErrorListItem);
foreach (var item in listItems)
{
if (item.ErrorSeverity == ErrorSeverity.Info)
continue;
var startOffset = document.GetOffset(new TextLocation(item.StartLine + 1, item.StartColumn + 1));
var endOffset = document.GetOffset(new TextLocation(item.EndLine + 1, item.EndColumn + 1));
if (item.ErrorSeverity == ErrorSeverity.Error)
Create(startOffset, endOffset - startOffset, item.Description, Colors.Red);
else
Create(startOffset, endOffset - startOffset, item.Description, Colors.DarkGreen);
}
}
public KnownLayer Layer => KnownLayer.Selection;
public void Create(int offset, int length, string message, Color color)
{
var marker = new TextMarker(offset, length, message, color);
markers.Add(marker);
textEditor.TextArea.TextView.Redraw(marker);
}
public void Clear()
{
var oldMarkers = markers.ToArray();
markers.Clear();
foreach (TextMarker m in oldMarkers)
{
textEditor.TextArea.TextView.Redraw(m);
}
}
public void Draw(TextView textView, DrawingContext drawingContext)
{
if (!markers.Any() || !textView.VisualLinesValid)
{
return;
}
var visualLines = textView.VisualLines;
if (visualLines.Count == 0)
{
return;
}
int viewStart = visualLines.First().FirstDocumentLine.Offset;
int viewEnd = visualLines.Last().LastDocumentLine.EndOffset;
foreach (TextMarker marker in markers.FindOverlappingSegments(viewStart, viewEnd - viewStart))
{
foreach (Rect rect in BackgroundGeometryBuilder.GetRectsForSegment(textView, marker))
{
Point startPoint = rect.BottomLeft;
Point endPoint = rect.BottomRight;
var pen = new Pen(new SolidColorBrush(marker.MarkerColor), 1);
pen.Freeze();
const double offset = 2.5;
int count = Math.Max((int)((endPoint.X - startPoint.X) / offset) + 1, 4);
var geometry = new StreamGeometry();
using (StreamGeometryContext ctx = geometry.Open())
{
ctx.BeginFigure(startPoint, false, false);
ctx.PolyLineTo(CreatePoints(startPoint, offset, count).ToArray(), true, false);
}
geometry.Freeze();
drawingContext.DrawGeometry(Brushes.Transparent, pen, geometry);
break;
}
}
}
public void Transform(ITextRunConstructionContext context, IList<VisualLineElement> elements)
{
}
private void TextViewMouseHover(object sender, MouseEventArgs e)
{
if (!markers.Any()) { return; }
TextViewPosition? position = textEditor.TextArea.TextView.GetPositionFloor(
e.GetPosition(textEditor.TextArea.TextView) + textEditor.TextArea.TextView.ScrollOffset);
if (position.HasValue)
{
TextLocation logicalPosition = position.Value.Location;
int offset = textEditor.Document.GetOffset(logicalPosition);
var markersAtOffset = markers.FindSegmentsContaining(offset);
TextMarker marker = markersAtOffset.LastOrDefault(m => !string.IsNullOrEmpty(m.Message));
if (marker != null)
{
if (toolTip == null)
{
toolTip = new ToolTip();
toolTip.Closed += (s2, e2) => toolTip = null;
toolTip.PlacementTarget = textEditor;
toolTip.Content = new TextBlock
{
Text = marker.Message,
TextWrapping = TextWrapping.Wrap
};
toolTip.IsOpen = true;
e.Handled = true;
}
}
}
}
private void TextViewMouseHoverStopped(object sender, MouseEventArgs e)
{
if (toolTip != null)
{
toolTip.IsOpen = false;
e.Handled = true;
}
}
private void TextViewVisualLinesChanged(object sender, EventArgs e)
{
//if (toolTip != null)
//{
// toolTip.IsOpen = false;
//}
}
private static IEnumerable<Point> CreatePoints(Point start, double offset, int count)
{
for (int i = 0; i < count; i++)
{
yield return new Point(start.X + (i * offset), start.Y - ((i + 1) % 2 == 0 ? offset : 0));
}
}
}
}