添加项目文件。
This commit is contained in:
137
Szmedi.RvKits/ScriptPad/editor/BraceFoldingStrategy.cs
Normal file
137
Szmedi.RvKits/ScriptPad/editor/BraceFoldingStrategy.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
52
Szmedi.RvKits/ScriptPad/editor/CSIndentationStrategy.cs
Normal file
52
Szmedi.RvKits/ScriptPad/editor/CSIndentationStrategy.cs
Normal 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)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
44
Szmedi.RvKits/ScriptPad/editor/CodeCompletionData.cs
Normal file
44
Szmedi.RvKits/ScriptPad/editor/CodeCompletionData.cs
Normal 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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
42
Szmedi.RvKits/ScriptPad/editor/TextContainer.cs
Normal file
42
Szmedi.RvKits/ScriptPad/editor/TextContainer.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
20
Szmedi.RvKits/ScriptPad/editor/TextMarker.cs
Normal file
20
Szmedi.RvKits/ScriptPad/editor/TextMarker.cs
Normal 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; }
|
||||
}
|
||||
}
|
||||
196
Szmedi.RvKits/ScriptPad/editor/TextMarkerService.cs
Normal file
196
Szmedi.RvKits/ScriptPad/editor/TextMarkerService.cs
Normal 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));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user