添加项目文件。
32
Szmedi.RvKits/ScriptPad/AddReferenceWindow.xaml
Normal file
@@ -0,0 +1,32 @@
|
||||
<Window
|
||||
x:Class="ScriptPad.AddReferenceWindow"
|
||||
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:local="clr-namespace:ScriptPad"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
Title="程序集引用"
|
||||
Width="600"
|
||||
Height="400"
|
||||
Background="{DynamicResource MaterialDesign.Brush.Background}"
|
||||
TextElement.Foreground="{DynamicResource MaterialDesign.Brush.Foreground}"
|
||||
mc:Ignorable="d">
|
||||
<Window.Resources>
|
||||
<ResourceDictionary>
|
||||
<ResourceDictionary.MergedDictionaries>
|
||||
<ResourceDictionary Source="pack://application:,,,/Szmedi.RvKits;component/WPFUI.xaml" />
|
||||
</ResourceDictionary.MergedDictionaries>
|
||||
</ResourceDictionary>
|
||||
</Window.Resources>
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="*" />
|
||||
<RowDefinition Height="auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<ListBox Name="ReferenceList" Grid.Row="0" />
|
||||
<StackPanel Grid.Row="1" HorizontalAlignment="Right" Orientation="Horizontal">
|
||||
<Button Name="okBtn" Click="OkBtn_Click" Content="确定" />
|
||||
<Button Name="cancelBtn" Click="CancelBtn_Click" Content="取消" />
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</Window>
|
||||
70
Szmedi.RvKits/ScriptPad/AddReferenceWindow.xaml.cs
Normal file
@@ -0,0 +1,70 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
|
||||
namespace ScriptPad
|
||||
{
|
||||
/// <summary>
|
||||
/// AddReferenceWindow.xaml 的交互逻辑
|
||||
/// </summary>
|
||||
public partial class AddReferenceWindow : Window
|
||||
{
|
||||
private CsScript script;
|
||||
private List<string> list;
|
||||
|
||||
public AddReferenceWindow(CsScript script)
|
||||
{
|
||||
this.script = script;
|
||||
InitializeComponent();
|
||||
|
||||
this.list = script.GetReferences().OfType<Microsoft.CodeAnalysis.PortableExecutableReference>().Select(p => p.FilePath).ToList();
|
||||
|
||||
var path = typeof(object).Assembly.Location;
|
||||
path = Path.GetDirectoryName(path);
|
||||
|
||||
var dir = new DirectoryInfo(path);
|
||||
|
||||
var dic = dir.GetFiles().Where(p => p.Extension == ".dll").ToDictionary(p => p.Name);
|
||||
|
||||
foreach (var item in dic.Keys.OrderBy(p => p.Substring(0, p.Length - 4)))
|
||||
{
|
||||
var cb = new CheckBox();
|
||||
cb.Content = item;
|
||||
cb.IsThreeState = false;
|
||||
cb.ToolTip = dic[item].FullName;
|
||||
|
||||
if (list.Contains(dic[item].FullName))
|
||||
cb.IsChecked = true;
|
||||
|
||||
ReferenceList.Items.Add(cb);
|
||||
}
|
||||
}
|
||||
|
||||
private void CancelBtn_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
this.Close();
|
||||
}
|
||||
|
||||
private void OkBtn_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
foreach (var item in ReferenceList.Items)
|
||||
{
|
||||
var cb = item as CheckBox;
|
||||
var path = cb.ToolTip as string;
|
||||
|
||||
if(list.Contains(path) && !cb.IsChecked.Value)
|
||||
{
|
||||
this.script.RemoveReference(path);
|
||||
}
|
||||
if(!list.Contains(path) && cb.IsChecked.Value)
|
||||
{
|
||||
this.script.AddReference(path);
|
||||
}
|
||||
}
|
||||
this.Close();
|
||||
}
|
||||
}
|
||||
}
|
||||
137
Szmedi.RvKits/ScriptPad/CodeEditor.xaml
Normal file
@@ -0,0 +1,137 @@
|
||||
<UserControl
|
||||
x:Class="ScriptPad.CodeEditor"
|
||||
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:editor="http://icsharpcode.net/sharpdevelop/avalonedit"
|
||||
xmlns:local="clr-namespace:ScriptPad"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:md="http://materialdesigninxaml.net/winfx/xaml/themes"
|
||||
d:DesignHeight="450"
|
||||
d:DesignWidth="800"
|
||||
Background="{DynamicResource MaterialDesign.Brush.Background}"
|
||||
TextElement.Foreground="{DynamicResource MaterialDesign.Brush.Foreground}"
|
||||
mc:Ignorable="d">
|
||||
<UserControl.Resources>
|
||||
<ResourceDictionary>
|
||||
<ResourceDictionary.MergedDictionaries>
|
||||
<ResourceDictionary Source="/Szmedi.RvKits;component/ScriptPad/resource/ImageResource.xaml" />
|
||||
<ResourceDictionary Source="pack://application:,,,/Szmedi.RvKits;component/WPFUI.xaml" />
|
||||
</ResourceDictionary.MergedDictionaries>
|
||||
</ResourceDictionary>
|
||||
</UserControl.Resources>
|
||||
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="auto" />
|
||||
<RowDefinition Height="*" />
|
||||
<RowDefinition Height="auto" />
|
||||
<RowDefinition Height="20" MaxHeight="500" />
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<ToolBar Grid.Row="0" ToolBarTray.IsLocked="True">
|
||||
<ToolBar.Template>
|
||||
<ControlTemplate TargetType="ToolBar">
|
||||
<ToolBarPanel Background="{TemplateBinding Background}" IsItemsHost="True" />
|
||||
</ControlTemplate>
|
||||
</ToolBar.Template>
|
||||
<ToolBar.Resources>
|
||||
<Style TargetType="{x:Type Image}">
|
||||
<Setter Property="Height" Value="16" />
|
||||
<Setter Property="Margin" Value="1" />
|
||||
<Style.Triggers>
|
||||
<DataTrigger Binding="{Binding RelativeSource={RelativeSource AncestorType={x:Type ButtonBase}, AncestorLevel=1}, Path=IsEnabled}" Value="False">
|
||||
<Setter Property="Opacity" Value="0.30" />
|
||||
</DataTrigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
</ToolBar.Resources>
|
||||
|
||||
<Button
|
||||
Name="OpenFile_btn"
|
||||
Margin="3"
|
||||
Click="OpenFile_btn_Click"
|
||||
ToolTip="从文件加载">
|
||||
<md:PackIcon Kind="File" />
|
||||
<!--<Image Source="/ScriptPad;component/Resource/Images/OpenFile_16x.png" />-->
|
||||
</Button>
|
||||
<Button Name="SaveFile_btn" Click="SaveFile_btn_Click" ToolTip="保存脚本">
|
||||
<!--<Image Source="/ScriptPad;component/Resource/Images/Save_16x.png" />-->
|
||||
<md:PackIcon Kind="ContentSave" />
|
||||
</Button>
|
||||
<Button Command="Undo" ToolTip="撤销">
|
||||
<md:PackIcon Kind="Undo" />
|
||||
</Button>
|
||||
<Button Command="Redo" ToolTip="重做">
|
||||
<md:PackIcon Kind="Redo" />
|
||||
</Button>
|
||||
<Separator />
|
||||
<Button Name="runBtn" Click="runBtn_Click" ToolTip="运行">
|
||||
<md:PackIcon Kind="Play" />
|
||||
</Button>
|
||||
<Separator />
|
||||
<Button Name="formatBtn" Click="formatBtn_Click">
|
||||
<md:PackIcon Kind="FormatText" />
|
||||
</Button>
|
||||
<Button Name="CommentBtn" Click="CommentBtn_Click">
|
||||
<md:PackIcon Kind="CommentOutline" />
|
||||
</Button>
|
||||
<Button Name="UnCommentBtn" Click="UnCommentBtn_Click">
|
||||
<md:PackIcon Kind="CommentOffOutline" />
|
||||
</Button>
|
||||
<Separator />
|
||||
<Button Name="AddReference" Click="Reference_Click" ToolTip="引用">
|
||||
<md:PackIcon Kind="Attachment" />
|
||||
</Button>
|
||||
</ToolBar>
|
||||
|
||||
<editor:TextEditor
|
||||
x:Name="codeEditor"
|
||||
Grid.Row="1"
|
||||
Background="{DynamicResource MaterialDesign.Brush.Background}"
|
||||
FontFamily="Consolas"
|
||||
FontSize="14"
|
||||
Foreground="{DynamicResource MaterialDesign.Brush.Foreground}"
|
||||
ShowLineNumbers="True"
|
||||
SyntaxHighlighting="C#">
|
||||
<editor:TextEditor.ContextMenu>
|
||||
<ContextMenu>
|
||||
<MenuItem Command="Find" Header="查找">
|
||||
<MenuItem.Icon>
|
||||
<md:PackIcon Kind="Search" />
|
||||
</MenuItem.Icon>
|
||||
</MenuItem>
|
||||
<Separator />
|
||||
<MenuItem Command="Cut" Header="剪切">
|
||||
<MenuItem.Icon>
|
||||
<md:PackIcon Kind="ContentCut" />
|
||||
</MenuItem.Icon>
|
||||
</MenuItem>
|
||||
<MenuItem Command="Copy" Header="复制">
|
||||
<MenuItem.Icon>
|
||||
<md:PackIcon Kind="ContentCopy" />
|
||||
</MenuItem.Icon>
|
||||
</MenuItem>
|
||||
<MenuItem Command="Paste" Header="粘贴">
|
||||
<MenuItem.Icon>
|
||||
<md:PackIcon Kind="ContentPaste" />
|
||||
</MenuItem.Icon>
|
||||
</MenuItem>
|
||||
</ContextMenu>
|
||||
</editor:TextEditor.ContextMenu>
|
||||
</editor:TextEditor>
|
||||
<GridSplitter Grid.Row="2" Height="6" HorizontalAlignment="Stretch" />
|
||||
<RichTextBox
|
||||
Name="outTextbox"
|
||||
Grid.Row="3"
|
||||
FontFamily="Consolas"
|
||||
FontSize="14"
|
||||
VerticalScrollBarVisibility="Visible">
|
||||
<FlowDocument Name="flowDocument" />
|
||||
</RichTextBox>
|
||||
</Grid>
|
||||
</UserControl>
|
||||
300
Szmedi.RvKits/ScriptPad/CodeEditor.xaml.cs
Normal file
@@ -0,0 +1,300 @@
|
||||
using ICSharpCode.AvalonEdit.CodeCompletion;
|
||||
using ICSharpCode.AvalonEdit.Document;
|
||||
using ICSharpCode.AvalonEdit.Folding;
|
||||
using ICSharpCode.AvalonEdit.Highlighting;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Documents;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Threading;
|
||||
using ScriptPad.Editor;
|
||||
using ScriptPad.Roslyn;
|
||||
using System.Reactive;
|
||||
using System.Reactive.Linq;
|
||||
|
||||
namespace ScriptPad
|
||||
{
|
||||
/// <summary>
|
||||
/// CodeEditor.xaml 的交互逻辑
|
||||
/// </summary>
|
||||
public partial class CodeEditor : UserControl
|
||||
{
|
||||
private CompletionWindow completionWindow;
|
||||
private CancellationTokenSource completionCancellation;
|
||||
private TextMarkerService markerService;
|
||||
|
||||
public TextContainer Container { get; private set; }
|
||||
|
||||
private static int script;
|
||||
|
||||
public CsScript Script;
|
||||
|
||||
public CodeEditor(string path = null)
|
||||
{
|
||||
InitializeComponent();
|
||||
|
||||
// 需要提升效率, 暂时不用
|
||||
|
||||
codeEditor.TextArea.TextEntered += textEditor_TextArea_TextEntered;
|
||||
|
||||
if (string.IsNullOrEmpty(path))
|
||||
{
|
||||
script++;
|
||||
Script = new CsScript("script" + script, ScriptGlobals.templateScript);
|
||||
}
|
||||
else
|
||||
{
|
||||
Script = CsScript.CreateFromFile(path);
|
||||
}
|
||||
|
||||
this.codeEditor.Text = Script.Text;
|
||||
ICSharpCode.AvalonEdit.Search.SearchPanel.Install(codeEditor);
|
||||
|
||||
//codeEditor.TextArea.IndentationStrategy = new CSIndentationStrategy();
|
||||
|
||||
|
||||
var csFoldingStrategy = new CSharpFoldingStrategy();
|
||||
var foldingManager = FoldingManager.Install(codeEditor.TextArea);
|
||||
this.codeEditor.GetTextChangeds().StartWith(EventArgs.Empty)
|
||||
.Throttle(TimeSpan.FromMilliseconds(200))
|
||||
.ObserveOnDispatcher()
|
||||
.Subscribe(p => csFoldingStrategy.UpdateFoldings(foldingManager, codeEditor.Document));
|
||||
|
||||
this.codeEditor.TextArea.GetTextEnterings()
|
||||
.Where(p => p.Text.Length > 0 && !IsAllowedLanguageLetter(p.Text[0]))
|
||||
.Subscribe(p => completionWindow?.CompletionList.RequestInsertion(p));
|
||||
|
||||
// 需要提升效率
|
||||
markerService = new TextMarkerService(codeEditor, this.Script);
|
||||
|
||||
this.Container = new TextContainer(this.codeEditor.Document);
|
||||
Observable.FromEventPattern(this.Container, nameof(this.Container.TextChanged))
|
||||
.Subscribe(p => this.Script.UpdateText((p.Sender as TextContainer).CurrentText));
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 关闭代码编辑窗口
|
||||
/// </summary>
|
||||
internal void Close()
|
||||
{
|
||||
if (Script.IsChanged)
|
||||
{
|
||||
var result = MessageBox.Show("文件已修改, 是否保存?", "保存", MessageBoxButton.YesNoCancel);
|
||||
if (result == MessageBoxResult.OK)
|
||||
{
|
||||
Script.Save();
|
||||
}
|
||||
if (result == MessageBoxResult.Cancel)
|
||||
{
|
||||
throw new TaskCanceledException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private bool TryCompleteBracket(TextCompositionEventArgs e)
|
||||
{
|
||||
if (e.Text.Last() == '{')
|
||||
{
|
||||
codeEditor.Document.Insert(codeEditor.CaretOffset, "}");
|
||||
codeEditor.CaretOffset--;
|
||||
return true;
|
||||
}
|
||||
else if (e.Text.Last() == '(')
|
||||
{
|
||||
codeEditor.Document.Insert(codeEditor.CaretOffset, ")");
|
||||
codeEditor.CaretOffset--;
|
||||
return true;
|
||||
}
|
||||
if (e.Text.Last() == '[')
|
||||
{
|
||||
codeEditor.Document.Insert(codeEditor.CaretOffset, "]");
|
||||
codeEditor.CaretOffset--;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private async void textEditor_TextArea_TextEntered(object sender, TextCompositionEventArgs e)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (TryCompleteBracket(e))
|
||||
return;
|
||||
|
||||
char? triggerChar = e.Text.FirstOrDefault();
|
||||
|
||||
completionCancellation = new CancellationTokenSource();
|
||||
var position = codeEditor.CaretOffset;
|
||||
var cancellationToken = completionCancellation.Token;
|
||||
|
||||
var isTrigger = Service.ScriptCompletionService.IsTrigger(Script.ID, this.Container.CurrentText, position, triggerChar);
|
||||
if (completionWindow == null && (triggerChar == null || triggerChar == '.' || IsAllowedLanguageLetter(triggerChar.Value)))
|
||||
{
|
||||
var list = await Service.ScriptCompletionService.GetCompletionsAsync(Script.ID, position);
|
||||
if (!list.Any())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
completionWindow = new CompletionWindow(codeEditor.TextArea)
|
||||
{
|
||||
WindowStyle = WindowStyle.None,
|
||||
AllowsTransparency = true,
|
||||
MaxWidth = 340,
|
||||
Width = 340,
|
||||
MaxHeight = 206,
|
||||
Height = 206
|
||||
};
|
||||
foreach (var item in list)
|
||||
{
|
||||
completionWindow.CompletionList.CompletionData.Add(item);
|
||||
}
|
||||
|
||||
if (triggerChar == null || IsAllowedLanguageLetter(triggerChar.Value))
|
||||
{
|
||||
var word = GetWord(position);
|
||||
completionWindow.StartOffset = word.Item1;
|
||||
completionWindow.CompletionList.SelectItem(word.Item2);
|
||||
}
|
||||
completionWindow.Show();
|
||||
completionWindow.Closed += (s2, e2) =>
|
||||
{
|
||||
completionWindow = null;
|
||||
};
|
||||
}
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private Tuple<int, string> GetWord(int position)
|
||||
{
|
||||
var wordStart = TextUtilities.GetNextCaretPosition(codeEditor.TextArea.Document, position, LogicalDirection.Backward, CaretPositioningMode.WordStart);
|
||||
var text = codeEditor.TextArea.Document.GetText(wordStart, position - wordStart);
|
||||
return new Tuple<int, string>(wordStart, text);
|
||||
}
|
||||
|
||||
private static bool IsAllowedLanguageLetter(char character)
|
||||
{
|
||||
return TextUtilities.GetCharacterClass(character) == CharacterClass.IdentifierPart;
|
||||
}
|
||||
|
||||
private void OpenFile_btn_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
System.Windows.Forms.OpenFileDialog openFile = new System.Windows.Forms.OpenFileDialog()
|
||||
{
|
||||
Filter = "C# 脚本文件(*.csx)|*.csx"
|
||||
};
|
||||
if (openFile.ShowDialog() == System.Windows.Forms.DialogResult.OK)
|
||||
{
|
||||
this.Script = CsScript.CreateFromFile(openFile.FileName);
|
||||
this.codeEditor.Text = Script.Text;
|
||||
this.markerService.Script = Script;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private async void formatBtn_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
var changes = await Script.Format();
|
||||
if (changes.Any())
|
||||
{
|
||||
changes = changes.Reverse();
|
||||
codeEditor.Document.BeginUpdate();
|
||||
foreach (var item in changes)
|
||||
{
|
||||
codeEditor.Document.Replace(item.Span.Start, item.Span.Length, item.NewText, OffsetChangeMappingType.RemoveAndInsert);
|
||||
}
|
||||
codeEditor.Document.EndUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
private async void runBtn_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
await Run();
|
||||
}
|
||||
|
||||
public async Task Run()
|
||||
{
|
||||
try
|
||||
{
|
||||
flowDocument.Blocks.Clear();
|
||||
flowDocument.Blocks.Add(new Paragraph());
|
||||
Console.SetOut(new DelegateTextWriter((flowDocument.Blocks.First() as Paragraph).Inlines.Add));
|
||||
|
||||
await ScriptRunner.Run(Script);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine(ex);
|
||||
}
|
||||
}
|
||||
|
||||
private void SaveFile_btn_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
Script.Save();
|
||||
}
|
||||
|
||||
private void CommentBtn_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
var document = codeEditor.Document;
|
||||
var startLine = document.GetLineByOffset(codeEditor.SelectionStart);
|
||||
var endLine = document.GetLineByOffset(codeEditor.SelectionStart + codeEditor.SelectionLength);
|
||||
|
||||
document.BeginUpdate();
|
||||
var line = startLine;
|
||||
while (line != null && line.LineNumber <= endLine.LineNumber)
|
||||
{
|
||||
var whitespace = TextUtilities.GetLeadingWhitespace(document, line);
|
||||
if (line.Length > whitespace.Length)
|
||||
{
|
||||
var text = document.GetText(whitespace) + "//";
|
||||
document.Replace(whitespace.Offset, whitespace.Length, text, OffsetChangeMappingType.RemoveAndInsert);
|
||||
}
|
||||
line = line.NextLine;
|
||||
}
|
||||
document.EndUpdate();
|
||||
}
|
||||
|
||||
private void UnCommentBtn_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
var document = codeEditor.Document;
|
||||
var startLine = document.GetLineByOffset(codeEditor.SelectionStart);
|
||||
var endLine = document.GetLineByOffset(codeEditor.SelectionStart + codeEditor.SelectionLength);
|
||||
|
||||
document.BeginUpdate();
|
||||
var line = startLine;
|
||||
while (line != null && line.LineNumber <= endLine.LineNumber)
|
||||
{
|
||||
var whitespace = TextUtilities.GetLeadingWhitespace(document, line);
|
||||
if (line.Length > whitespace.Length + 2)
|
||||
{
|
||||
var text = document.GetText(whitespace.EndOffset, 2);
|
||||
if (text == "//")
|
||||
document.Remove(whitespace.EndOffset, 2);
|
||||
}
|
||||
line = line.NextLine;
|
||||
}
|
||||
document.EndUpdate();
|
||||
}
|
||||
|
||||
private void Reference_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
new ReferenceWindow(this.Script).ShowDialog();
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
195
Szmedi.RvKits/ScriptPad/CsScript.cs
Normal file
@@ -0,0 +1,195 @@
|
||||
using Microsoft.CodeAnalysis;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Forms;
|
||||
using ScriptPad.Roslyn;
|
||||
using Microsoft.CodeAnalysis.Text;
|
||||
|
||||
namespace ScriptPad
|
||||
{
|
||||
public class CsScript
|
||||
{
|
||||
/// <summary>
|
||||
/// 是否更改过
|
||||
/// </summary>
|
||||
public bool IsChanged { get; private set; }
|
||||
|
||||
public readonly DocumentId ID;
|
||||
public string Name { get; set; }
|
||||
public string Path { get; set; }
|
||||
|
||||
public string Text { get; private set; }
|
||||
|
||||
public IReadOnlyCollection<PortableExecutableReference> References => Workspace.GetReferences(this.ID).OfType<PortableExecutableReference>().ToList();
|
||||
|
||||
/// <summary>
|
||||
/// 创建具有指定名字和内容的脚本对象
|
||||
/// </summary>
|
||||
/// <param name="name"></param>
|
||||
/// <param name="text"></param>
|
||||
public CsScript(string name, string text)
|
||||
{
|
||||
this.Name = name;
|
||||
this.Text = text;
|
||||
if (text == null)
|
||||
this.Text = "";
|
||||
|
||||
ID = Workspace.AddProjectWithDocument(name, this.Text);
|
||||
IsChanged = false;
|
||||
}
|
||||
|
||||
private ScriptingWorkspace Workspace => ScriptingWorkspace.GetInstance();
|
||||
|
||||
/// <summary>
|
||||
/// 从文件创建 Script 对象
|
||||
/// </summary>
|
||||
/// <param name="path"></param>
|
||||
/// <returns></returns>
|
||||
public static CsScript CreateFromFile(string path)
|
||||
{
|
||||
var info = new FileInfo(path);
|
||||
var text = File.ReadAllLines(path);
|
||||
|
||||
var i = 0;
|
||||
List<string> references = new List<string>();
|
||||
for (; i < text.Length; i++)
|
||||
{
|
||||
if (text[i].StartsWith("#r "))
|
||||
{
|
||||
references.Add(text[i].Trim().Substring(4, text[i].Length - 5)); // #r "
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
var code = new StringBuilder();
|
||||
for (; i < text.Length; i++)
|
||||
{
|
||||
code.Append(text[i]);
|
||||
code.Append("\r\n");
|
||||
}
|
||||
var script = new CsScript(info.Name, code.ToString());
|
||||
foreach (var item in references)
|
||||
{
|
||||
script.AddReference(item);
|
||||
}
|
||||
script.Path = path;
|
||||
|
||||
return script;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 添加引用
|
||||
/// </summary>
|
||||
/// <param name="path">文件路径</param>
|
||||
public void AddReference(string path)
|
||||
{
|
||||
Workspace.AddReference(path, this.ID);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 删除引用
|
||||
/// </summary>
|
||||
/// <param name="path"></param>
|
||||
public void RemoveReference(string path)
|
||||
{
|
||||
Workspace.RemoveReference(path, this.ID);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 整理脚本
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public async Task<IEnumerable<Microsoft.CodeAnalysis.Text.TextChange>> Format()
|
||||
{
|
||||
var document = Workspace.GetDocument(ID);
|
||||
var formattedDocument = await Microsoft.CodeAnalysis.Formatting.Formatter.FormatAsync(document).ConfigureAwait(false);
|
||||
return await formattedDocument.GetTextChangesAsync(document);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取诊断信息
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public async Task<ImmutableArray<Diagnostic>> GetDiagnostics()
|
||||
{
|
||||
var project = Workspace.GetProject(ID);
|
||||
var compilation = await project.GetCompilationAsync();
|
||||
return compilation.GetDiagnostics();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取脚本内容
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public async Task<string> GetScriptText()
|
||||
{
|
||||
var text = await Workspace.GetDocument(ID).GetTextAsync();
|
||||
return text.ToString();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取脚本内容
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public string ToCode()
|
||||
{
|
||||
var code = new StringBuilder();
|
||||
foreach (var item in References.Select(p => p.FilePath).Except(ScriptGlobals.InitAssemblies.Select(p => p.Location)))
|
||||
{
|
||||
var refstr = $"#r \"{item}\"\r\n";
|
||||
code.Append(refstr);
|
||||
}
|
||||
|
||||
code.Append(GetScriptText().Result);
|
||||
return code.ToString();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 保存
|
||||
/// </summary>
|
||||
public void Save()
|
||||
{
|
||||
if (string.IsNullOrEmpty(Path))
|
||||
{
|
||||
var dialog = new SaveFileDialog()
|
||||
{
|
||||
Filter = "C# 脚本|*.csx",
|
||||
Title = "保存文件"
|
||||
};
|
||||
if (dialog.ShowDialog() == DialogResult.OK)
|
||||
{
|
||||
File.WriteAllText(dialog.FileName, ToCode());
|
||||
this.Path = dialog.FileName;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
File.WriteAllText(Path, ToCode());
|
||||
}
|
||||
IsChanged = false;
|
||||
}
|
||||
|
||||
public void UpdateText(Document document)
|
||||
{
|
||||
IsChanged = true;
|
||||
Workspace.TryApplyChanges(document.Project.Solution);
|
||||
}
|
||||
|
||||
public void UpdateText(SourceText sourceText)
|
||||
{
|
||||
Workspace.UpdateText(ID,sourceText);
|
||||
}
|
||||
|
||||
internal IEnumerable<MetadataReference> GetReferences()
|
||||
{
|
||||
return Workspace.GetReferences(this.ID);
|
||||
}
|
||||
}
|
||||
}
|
||||
44
Szmedi.RvKits/ScriptPad/DelegateTextWriter.cs
Normal file
@@ -0,0 +1,44 @@
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace ScriptPad
|
||||
{
|
||||
public class DelegateTextWriter : TextWriter
|
||||
{
|
||||
private readonly Action<string> appendTextAction;
|
||||
|
||||
public DelegateTextWriter(Action<string> appendTextAction) : base(CultureInfo.CurrentCulture)
|
||||
{
|
||||
this.appendTextAction = appendTextAction;
|
||||
}
|
||||
|
||||
public override Encoding Encoding => Encoding.UTF8;
|
||||
|
||||
public override void Write(char value)
|
||||
{
|
||||
appendTextAction(value.ToString());
|
||||
}
|
||||
|
||||
public override void Write(char[] buffer, int index, int count)
|
||||
{
|
||||
if (index != 0 || count != buffer.Length)
|
||||
{
|
||||
buffer = buffer.Skip(index).Take(count).ToArray();
|
||||
}
|
||||
appendTextAction(new string(buffer));
|
||||
}
|
||||
|
||||
public override void Write(string value)
|
||||
{
|
||||
appendTextAction(value);
|
||||
}
|
||||
|
||||
public override object InitializeLifetimeService()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
4
Szmedi.RvKits/ScriptPad/FodyWeavers.xml
Normal file
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd">
|
||||
<ReactiveUI />
|
||||
</Weavers>
|
||||
45
Szmedi.RvKits/ScriptPad/MainWindow.xaml
Normal file
@@ -0,0 +1,45 @@
|
||||
<Window
|
||||
x:Class="ScriptPad.MainWindow"
|
||||
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:local="clr-namespace:ScriptPad"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:md="http://materialdesigninxaml.net/winfx/xaml/themes"
|
||||
Title="C#脚本"
|
||||
Width="800"
|
||||
Height="450"
|
||||
Background="{DynamicResource MaterialDesign.Brush.Background}"
|
||||
Closing="Window_Closing"
|
||||
TextElement.Foreground="{DynamicResource MaterialDesign.Brush.Foreground}"
|
||||
UseLayoutRounding="True"
|
||||
mc:Ignorable="d">
|
||||
<Window.Resources>
|
||||
<ResourceDictionary>
|
||||
<ResourceDictionary.MergedDictionaries>
|
||||
<ResourceDictionary Source="pack://application:,,,/Szmedi.RvKits;component/WPFUI.xaml" />
|
||||
</ResourceDictionary.MergedDictionaries>
|
||||
<RoutedCommand x:Key="runCommand" />
|
||||
</ResourceDictionary>
|
||||
</Window.Resources>
|
||||
<Window.InputBindings>
|
||||
<KeyBinding Key="F5" Command="{StaticResource runCommand}" />
|
||||
</Window.InputBindings>
|
||||
<Window.CommandBindings>
|
||||
<CommandBinding Command="{StaticResource runCommand}" Executed="RunCommand_Executed" />
|
||||
</Window.CommandBindings>
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="*" />
|
||||
</Grid.RowDefinitions>
|
||||
<TabControl
|
||||
Name="tc"
|
||||
HorizontalContentAlignment="Left"
|
||||
Background="{DynamicResource MaterialDesign.Brush.Background}"
|
||||
Foreground="{DynamicResource MaterialDesign.Brush.Foreground}"
|
||||
SelectionChanged="TabControl_SelectionChanged"
|
||||
Style="{StaticResource MaterialDesignFilledUniformTabControl}">
|
||||
<TabItem Header="+" ToolTip="新建文件" />
|
||||
</TabControl>
|
||||
</Grid>
|
||||
</Window>
|
||||
120
Szmedi.RvKits/ScriptPad/MainWindow.xaml.cs
Normal file
@@ -0,0 +1,120 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Data;
|
||||
using System.Windows.Documents;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Imaging;
|
||||
using System.Windows.Navigation;
|
||||
using System.Windows.Shapes;
|
||||
|
||||
namespace ScriptPad
|
||||
{
|
||||
/// <summary>
|
||||
/// MainWindow.xaml 的交互逻辑
|
||||
/// </summary>
|
||||
public partial class MainWindow : Window
|
||||
{
|
||||
public MainWindow()
|
||||
{
|
||||
InitializeComponent();
|
||||
|
||||
//if (string.IsNullOrWhiteSpace(Properties.Settings.Default.WorkFolder))
|
||||
//{
|
||||
// Properties.Settings.Default.WorkFolder = Environment.CurrentDirectory;
|
||||
//}
|
||||
//var dir = new System.IO.DirectoryInfo(Properties.Settings.Default.WorkFolder);
|
||||
//treeRoot.Header = dir.FullName;
|
||||
//treeRoot.IsExpanded = true;
|
||||
|
||||
//foreach (var item in dir.EnumerateFiles())
|
||||
//{
|
||||
// if (item.Extension == ".csx")
|
||||
// {
|
||||
// this.treeRoot.Items.Add(new TreeViewItem() { Header = item.Name });
|
||||
// }
|
||||
//}
|
||||
|
||||
AddEditor();
|
||||
}
|
||||
|
||||
private void TabControl_SelectionChanged(object sender, SelectionChangedEventArgs e)
|
||||
{
|
||||
if (tc.SelectedIndex == tc.Items.Count - 1)
|
||||
{
|
||||
AddEditor();
|
||||
}
|
||||
}
|
||||
|
||||
private void AddEditor(string path = null)
|
||||
{
|
||||
var editor = new CodeEditor();
|
||||
var tb = new TextBlock();
|
||||
tb.Text = editor.Script.Name;
|
||||
tb.ContextMenu = new ContextMenu();
|
||||
var menuItem = new MenuItem();
|
||||
menuItem.Header = "关闭";
|
||||
tb.ContextMenu.Items.Add(menuItem);
|
||||
|
||||
|
||||
var tabitem = (TabItem)tc.Items[tc.Items.Count - 1];
|
||||
tabitem.Header = tb;// editor.Script.Name;
|
||||
tabitem.Content = editor;
|
||||
|
||||
menuItem.Click += (sender, e) =>
|
||||
{
|
||||
CloseTab(tabitem);
|
||||
};
|
||||
|
||||
var tab = new TabItem() { Header = "+" };
|
||||
tc.Items.Add(tab);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 关闭标签页
|
||||
/// </summary>
|
||||
/// <param name="tab"></param>
|
||||
private void CloseTab(TabItem tab)
|
||||
{
|
||||
try
|
||||
{
|
||||
var codeEditor = tab.Content as CodeEditor;
|
||||
codeEditor.Close();
|
||||
tc.Items.Remove(tab);
|
||||
if (tc.Items.Count == 1)
|
||||
{
|
||||
AddEditor();
|
||||
}
|
||||
}
|
||||
catch (TaskCanceledException)
|
||||
{
|
||||
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
MessageBox.Show(ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e)
|
||||
{
|
||||
var names = tc.Items.Cast<TabItem>()
|
||||
.Select(tab => tab.Content as CodeEditor)
|
||||
.Where(editor => editor != null)
|
||||
.Select(editor => editor.Script.Name)
|
||||
.ToList();
|
||||
}
|
||||
|
||||
private async void RunCommand_Executed(object sender, ExecutedRoutedEventArgs e)
|
||||
{
|
||||
var tabItem = this.tc.SelectedItem as TabItem;
|
||||
var codeEditor = tabItem.Content as CodeEditor;
|
||||
await codeEditor.Run();
|
||||
}
|
||||
}
|
||||
}
|
||||
55
Szmedi.RvKits/ScriptPad/OpenScriptPadCommand.cs
Normal file
@@ -0,0 +1,55 @@
|
||||
using Autodesk.Revit.DB;
|
||||
using Autodesk.Revit.UI;
|
||||
|
||||
using Nice3point.Revit.Toolkit.External;
|
||||
|
||||
using RevitTools.Utils.Revit;
|
||||
|
||||
using ScriptPad;
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
|
||||
namespace Szmedi.RvKits.ScriptPad
|
||||
{
|
||||
[Autodesk.Revit.Attributes.Transaction(Autodesk.Revit.Attributes.TransactionMode.Manual)]
|
||||
[Autodesk.Revit.Attributes.Regeneration(Autodesk.Revit.Attributes.RegenerationOption.Manual)]
|
||||
public class OpenScriptPadCommand : ExternalCommand
|
||||
{
|
||||
private static ExEventUtils ExEventUtils;
|
||||
|
||||
public override void Execute()
|
||||
{
|
||||
var assembly = typeof(OpenScriptPadCommand).Assembly;
|
||||
var dir = Path.GetDirectoryName(assembly.Location);
|
||||
|
||||
ExEventUtils ??= ExEventUtils.GetOrCreate();
|
||||
|
||||
ScriptGlobals.GlobalObject = new RevitGlobals()
|
||||
{
|
||||
commandData = ExternalCommandData,
|
||||
uiApp = UiApplication,
|
||||
ExEventUtils = ExEventUtils
|
||||
};
|
||||
|
||||
ScriptGlobals.StartScript = "ExEventUtils.InvokeAsync(main)";
|
||||
ScriptGlobals.templateScript = File.ReadAllText(Path.Combine(dir,"Templates", "template.txt"));
|
||||
ScriptGlobals.InitAssemblies = new List<Assembly>
|
||||
{
|
||||
typeof(object).Assembly, // mscorelib
|
||||
typeof(Uri).Assembly, // System
|
||||
typeof(Enumerable).Assembly, // System.Core
|
||||
typeof(Element).Assembly, // Autodesk.Revit.DB
|
||||
typeof(UIApplication).Assembly // Autodesk.Revit.UI
|
||||
};
|
||||
|
||||
var window = new MainWindow();
|
||||
new System.Windows.Interop.WindowInteropHelper(window).Owner = Autodesk.Windows.ComponentManager.ApplicationWindow;
|
||||
window.Show();
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
67
Szmedi.RvKits/ScriptPad/ReferenceWindow.xaml
Normal file
@@ -0,0 +1,67 @@
|
||||
<Window
|
||||
x:Class="ScriptPad.ReferenceWindow"
|
||||
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:local="clr-namespace:ScriptPad"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
Title="引用"
|
||||
Width="600"
|
||||
Height="450"
|
||||
Background="{DynamicResource MaterialDesign.Brush.Background}"
|
||||
TextElement.Foreground="{DynamicResource MaterialDesign.Brush.Foreground}"
|
||||
mc:Ignorable="d">
|
||||
<Window.Resources>
|
||||
<ResourceDictionary>
|
||||
<ResourceDictionary.MergedDictionaries>
|
||||
<ResourceDictionary Source="pack://application:,,,/Szmedi.RvKits;component/WPFUI.xaml" />
|
||||
</ResourceDictionary.MergedDictionaries>
|
||||
</ResourceDictionary>
|
||||
</Window.Resources>
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="*" />
|
||||
<RowDefinition Height="auto" />
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<ListBox Name="ReferenceList" />
|
||||
<Grid Grid.Row="1">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="auto" />
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<Button
|
||||
Name="addBtn"
|
||||
Width="auto"
|
||||
Height="auto"
|
||||
Click="AddBtn_Click"
|
||||
Content="管理程序集引用" />
|
||||
<Button
|
||||
Name="browseBtn"
|
||||
Width="auto"
|
||||
Height="auto"
|
||||
Click="BrowseBtn_Click"
|
||||
Content="浏览..." />
|
||||
|
||||
<Button
|
||||
Name="deleteBtn"
|
||||
Width="auto"
|
||||
Height="auto"
|
||||
Click="DeleteBtn_Click"
|
||||
Content="删除" />
|
||||
|
||||
</StackPanel>
|
||||
<StackPanel Grid.Column="2" Orientation="Horizontal">
|
||||
<Button
|
||||
Name="CloseBtn"
|
||||
Grid.Column="5"
|
||||
Width="auto"
|
||||
Height="auto"
|
||||
Click="Close_Click"
|
||||
Content="关闭" />
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Window>
|
||||
92
Szmedi.RvKits/ScriptPad/ReferenceWindow.xaml.cs
Normal file
@@ -0,0 +1,92 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Data;
|
||||
using System.Windows.Documents;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Imaging;
|
||||
using System.Windows.Shapes;
|
||||
using System.IO;
|
||||
|
||||
namespace ScriptPad
|
||||
{
|
||||
/// <summary>
|
||||
/// ReferenceWindow.xaml 的交互逻辑
|
||||
/// </summary>
|
||||
public partial class ReferenceWindow : Window
|
||||
{
|
||||
private CsScript script;
|
||||
|
||||
public ReferenceWindow(CsScript script)
|
||||
{
|
||||
InitializeComponent();
|
||||
this.script = script;
|
||||
ShowReferences();
|
||||
}
|
||||
|
||||
private void BrowseBtn_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
System.Windows.Forms.OpenFileDialog openFileDialog = new System.Windows.Forms.OpenFileDialog()
|
||||
{
|
||||
Filter = "dll文件|*.dll|exe文件|*.exe",
|
||||
Multiselect = true
|
||||
};
|
||||
|
||||
if (openFileDialog.ShowDialog() == System.Windows.Forms.DialogResult.OK)
|
||||
{
|
||||
foreach (var item in openFileDialog.FileNames)
|
||||
{
|
||||
try
|
||||
{
|
||||
script.AddReference(item);
|
||||
}
|
||||
catch(Exception ex)
|
||||
{
|
||||
System.Windows.Forms.MessageBox.Show(ex.Message);
|
||||
}
|
||||
}
|
||||
ShowReferences();
|
||||
}
|
||||
}
|
||||
|
||||
private void ShowReferences()
|
||||
{
|
||||
ReferenceList.Items.Clear();
|
||||
foreach (var item in script.References)
|
||||
{
|
||||
var path = item.FilePath;
|
||||
ReferenceList.Items.Add(path);
|
||||
}
|
||||
}
|
||||
|
||||
private void DeleteBtn_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
var items = ReferenceList.SelectedItems.OfType<string>().ToList();
|
||||
foreach (var item in items)
|
||||
{
|
||||
ReferenceList.Items.Remove(item);
|
||||
script.RemoveReference(item);
|
||||
}
|
||||
if(items.Count >0)
|
||||
{
|
||||
ShowReferences();
|
||||
}
|
||||
}
|
||||
|
||||
private void Close_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
this.Close();
|
||||
}
|
||||
|
||||
private void AddBtn_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
new AddReferenceWindow(this.script).ShowDialog();
|
||||
ShowReferences();
|
||||
}
|
||||
}
|
||||
}
|
||||
13
Szmedi.RvKits/ScriptPad/RevitGlobals.cs
Normal file
@@ -0,0 +1,13 @@
|
||||
using Autodesk.Revit.UI;
|
||||
|
||||
using RevitTools.Utils.Revit;
|
||||
|
||||
namespace Szmedi.RvKits.ScriptPad
|
||||
{
|
||||
public class RevitGlobals
|
||||
{
|
||||
public ExternalCommandData commandData;
|
||||
public UIApplication uiApp;
|
||||
public ExEventUtils ExEventUtils;
|
||||
}
|
||||
}
|
||||
20
Szmedi.RvKits/ScriptPad/ScriptGlobals.cs
Normal file
@@ -0,0 +1,20 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace ScriptPad
|
||||
{
|
||||
public static class ScriptGlobals
|
||||
{
|
||||
public static object GlobalObject = new object();
|
||||
|
||||
public static string StartScript;
|
||||
|
||||
public static string templateScript;
|
||||
|
||||
public static List<Assembly> InitAssemblies;
|
||||
}
|
||||
}
|
||||
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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
80
Szmedi.RvKits/ScriptPad/resource/ImageResource.cs
Normal file
@@ -0,0 +1,80 @@
|
||||
using Microsoft.CodeAnalysis.Tags;
|
||||
using System;
|
||||
using System.Collections.Immutable;
|
||||
using System.Linq;
|
||||
using System.Windows;
|
||||
using System.Windows.Media;
|
||||
|
||||
namespace ScriptPad
|
||||
{
|
||||
public class ImageResource
|
||||
{
|
||||
private static ResourceDictionary ResourceDictionary = new Lazy<ResourceDictionary>(() =>
|
||||
{
|
||||
var dic = new ResourceDictionary();
|
||||
dic.Source = new Uri("/Szmedi.RvKits;component/ScriptPad/resource/ImageResource.xaml", UriKind.Relative);
|
||||
return dic;
|
||||
}, true).Value;
|
||||
|
||||
private static ImageSource GetImage(string resourceKey)
|
||||
{
|
||||
return (ImageSource)ResourceDictionary[resourceKey];
|
||||
}
|
||||
|
||||
public static ImageSource GetImage(ImmutableArray<string> tags)
|
||||
{
|
||||
var tag = tags.FirstOrDefault();
|
||||
if (tag == null) { return null; }
|
||||
|
||||
switch (tag)
|
||||
{
|
||||
case WellKnownTags.Class:
|
||||
return GetImage("ClassImageSource");
|
||||
|
||||
case WellKnownTags.Constant:
|
||||
return GetImage("ConstantImageSource");
|
||||
|
||||
case WellKnownTags.Delegate:
|
||||
return GetImage("DelegateImageSource");
|
||||
|
||||
case WellKnownTags.Enum:
|
||||
return GetImage("EnumImageSource");
|
||||
|
||||
case WellKnownTags.EnumMember:
|
||||
return GetImage("EnumItemImageSource");
|
||||
|
||||
case WellKnownTags.Event:
|
||||
return GetImage("EventImageSource");
|
||||
|
||||
case WellKnownTags.ExtensionMethod:
|
||||
return GetImage("ExtensionMethodImageSource");
|
||||
|
||||
case WellKnownTags.Field:
|
||||
return GetImage("FieldImageSource");
|
||||
|
||||
case WellKnownTags.Interface:
|
||||
return GetImage("InterfaceImageSource");
|
||||
|
||||
case WellKnownTags.Keyword:
|
||||
return GetImage("KeywordImageSource");
|
||||
|
||||
case WellKnownTags.Method:
|
||||
return GetImage("MethodImageSource");
|
||||
|
||||
case WellKnownTags.Module:
|
||||
return GetImage("ModuleImageSource");
|
||||
|
||||
case WellKnownTags.Namespace:
|
||||
return GetImage("NamespaceImageSource");
|
||||
|
||||
case WellKnownTags.Property:
|
||||
return GetImage("PropertyImageSource");
|
||||
|
||||
case WellKnownTags.Structure:
|
||||
return GetImage("StructureImageSource");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
103
Szmedi.RvKits/ScriptPad/resource/ImageResource.xaml
Normal file
@@ -0,0 +1,103 @@
|
||||
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
|
||||
|
||||
<!-- 工具栏图标 -->
|
||||
<Image
|
||||
x:Key="NewFileImage"
|
||||
Width="16"
|
||||
Height="16"
|
||||
Source="Images/NewFile_16x.png" />
|
||||
<Image
|
||||
x:Key="AddReference"
|
||||
Width="16"
|
||||
Height="16"
|
||||
Source="Images/AddReference_16x.png" />
|
||||
<Image
|
||||
x:Key="CommentImage"
|
||||
Width="16"
|
||||
Height="16"
|
||||
Source="Images/CommentCode_16x.png" />
|
||||
<Image
|
||||
x:Key="FarmatImage"
|
||||
Width="16"
|
||||
Height="16"
|
||||
Source="Images/FormatDocument_16x.png" />
|
||||
<Image
|
||||
x:Key="OpenFileImage"
|
||||
Width="16"
|
||||
Height="16"
|
||||
Source="Images/OpenFile_16x.png" />
|
||||
<Image
|
||||
x:Key="OpenFolderImage"
|
||||
Width="16"
|
||||
Height="16"
|
||||
Source="Images/OpenFolder_16x.png" />
|
||||
<Image
|
||||
x:Key="RanImage"
|
||||
Width="16"
|
||||
Height="16"
|
||||
Source="Images/Run_16x.png" />
|
||||
<Image
|
||||
x:Key="RedoImage"
|
||||
Width="16"
|
||||
Height="16"
|
||||
Source="Images/Redo_16x.png" />
|
||||
<Image
|
||||
x:Key="SaveImage"
|
||||
Width="16"
|
||||
Height="16"
|
||||
Source="Images/Save_16x.png" />
|
||||
<!--<Image
|
||||
x:Key="StopImage"
|
||||
Width="16"
|
||||
Height="16"
|
||||
Source="Images/Stop_16x.png" />-->
|
||||
<Image
|
||||
x:Key="UndoImage"
|
||||
Width="16"
|
||||
Height="16"
|
||||
Source="Images/Undo_16x.png" />
|
||||
<Image
|
||||
x:Key="UnCommentImage"
|
||||
Width="16"
|
||||
Height="16"
|
||||
Source="Images/UnCommentCode_16x.png" />
|
||||
|
||||
<!-- 右键菜单图标 -->
|
||||
<Image
|
||||
x:Key="Search"
|
||||
Width="16"
|
||||
Height="16"
|
||||
Source="Images/Search_16x.png" />
|
||||
<Image
|
||||
x:Key="CutImage"
|
||||
Width="16"
|
||||
Height="16"
|
||||
Source="Images/Cut_16x.png" />
|
||||
<Image
|
||||
x:Key="CopyImage"
|
||||
Width="16"
|
||||
Height="16"
|
||||
Source="Images/Copy_16x.png" />
|
||||
<Image
|
||||
x:Key="PasteImage"
|
||||
Width="16"
|
||||
Height="16"
|
||||
Source="Images/Paste_16x.png" />
|
||||
|
||||
<!-- 自动提示图标 -->
|
||||
<!--<ImageSource x:Key="AbstractClassImageSource">Images/AbstraceClass_16x.png</ImageSource>-->
|
||||
<ImageSource x:Key="ClassImageSource">Images/Class_16x.png</ImageSource>
|
||||
<ImageSource x:Key="ConstantImageSource">Images/Constant_16x.png</ImageSource>
|
||||
<ImageSource x:Key="DelegateImageSource">Images/Delegate_16x.png</ImageSource>
|
||||
<ImageSource x:Key="EnumImageSource">Images/Enumerator_16x.png</ImageSource>
|
||||
<ImageSource x:Key="EnumItemImageSource">Images/EnumItem_16x.png</ImageSource>
|
||||
<!--<ImageSource x:Key="ExtersionMethodImageSource">Images/ExtersionMethod_16x.png</ImageSource>-->
|
||||
<ImageSource x:Key="FieldImageSource">Images/Field_16x.png</ImageSource>
|
||||
<ImageSource x:Key="InterfaceImageSource">Images/Interface_16x.png</ImageSource>
|
||||
<ImageSource x:Key="KeywordImageSource">Images/KeywordSnippet_16x.png</ImageSource>
|
||||
<ImageSource x:Key="MethodImageSource">Images/Method_16x.png</ImageSource>
|
||||
<ImageSource x:Key="ModuleImageSource">Images/Module_16x.png</ImageSource>
|
||||
<ImageSource x:Key="NamespaceImageSource">Images/Namespace_16x.png</ImageSource>
|
||||
<ImageSource x:Key="PropertyImageSource">Images/Property_16x.png</ImageSource>
|
||||
<ImageSource x:Key="StructureImageSource">Images/Structure_16x.png</ImageSource>
|
||||
</ResourceDictionary>
|
||||
BIN
Szmedi.RvKits/ScriptPad/resource/Images/ASX_Paste_blue_16x.png
Normal file
|
After Width: | Height: | Size: 231 B |
BIN
Szmedi.RvKits/ScriptPad/resource/Images/ASX_Paste_grey_16x.png
Normal file
|
After Width: | Height: | Size: 231 B |
BIN
Szmedi.RvKits/ScriptPad/resource/Images/AbstractClass_16x.png
Normal file
|
After Width: | Height: | Size: 397 B |
BIN
Szmedi.RvKits/ScriptPad/resource/Images/AddReference_16x.png
Normal file
|
After Width: | Height: | Size: 169 B |
BIN
Szmedi.RvKits/ScriptPad/resource/Images/CS_16x.png
Normal file
|
After Width: | Height: | Size: 238 B |
BIN
Szmedi.RvKits/ScriptPad/resource/Images/Class_16x.png
Normal file
|
After Width: | Height: | Size: 212 B |
BIN
Szmedi.RvKits/ScriptPad/resource/Images/CommentCode_16x.png
Normal file
|
After Width: | Height: | Size: 200 B |
BIN
Szmedi.RvKits/ScriptPad/resource/Images/Constant_16x.png
Normal file
|
After Width: | Height: | Size: 260 B |
BIN
Szmedi.RvKits/ScriptPad/resource/Images/Copy_16x.png
Normal file
|
After Width: | Height: | Size: 314 B |
BIN
Szmedi.RvKits/ScriptPad/resource/Images/Cut_16x.png
Normal file
|
After Width: | Height: | Size: 319 B |
BIN
Szmedi.RvKits/ScriptPad/resource/Images/Delegate_16x.png
Normal file
|
After Width: | Height: | Size: 316 B |
BIN
Szmedi.RvKits/ScriptPad/resource/Images/EnumItem_16x.png
Normal file
|
After Width: | Height: | Size: 285 B |
BIN
Szmedi.RvKits/ScriptPad/resource/Images/Enumerator_16x.png
Normal file
|
After Width: | Height: | Size: 303 B |
BIN
Szmedi.RvKits/ScriptPad/resource/Images/ExtensionMethod_16x.png
Normal file
|
After Width: | Height: | Size: 542 B |
BIN
Szmedi.RvKits/ScriptPad/resource/Images/Field_16x.png
Normal file
|
After Width: | Height: | Size: 395 B |
BIN
Szmedi.RvKits/ScriptPad/resource/Images/FormatDocument_16x.png
Normal file
|
After Width: | Height: | Size: 246 B |
BIN
Szmedi.RvKits/ScriptPad/resource/Images/Interface_16x.png
Normal file
|
After Width: | Height: | Size: 222 B |
BIN
Szmedi.RvKits/ScriptPad/resource/Images/KeywordSnippet_16x.png
Normal file
|
After Width: | Height: | Size: 189 B |
BIN
Szmedi.RvKits/ScriptPad/resource/Images/Method_16x.png
Normal file
|
After Width: | Height: | Size: 649 B |
BIN
Szmedi.RvKits/ScriptPad/resource/Images/Module_16x.png
Normal file
|
After Width: | Height: | Size: 163 B |
BIN
Szmedi.RvKits/ScriptPad/resource/Images/Namespace_16x.png
Normal file
|
After Width: | Height: | Size: 486 B |
BIN
Szmedi.RvKits/ScriptPad/resource/Images/NewFile_16x.png
Normal file
|
After Width: | Height: | Size: 270 B |
BIN
Szmedi.RvKits/ScriptPad/resource/Images/OpenFile_16x.png
Normal file
|
After Width: | Height: | Size: 455 B |
BIN
Szmedi.RvKits/ScriptPad/resource/Images/OpenFolder_16x.png
Normal file
|
After Width: | Height: | Size: 457 B |
BIN
Szmedi.RvKits/ScriptPad/resource/Images/Paste_16x.png
Normal file
|
After Width: | Height: | Size: 311 B |
BIN
Szmedi.RvKits/ScriptPad/resource/Images/Property_16x.png
Normal file
|
After Width: | Height: | Size: 363 B |
BIN
Szmedi.RvKits/ScriptPad/resource/Images/Redo_16x.png
Normal file
|
After Width: | Height: | Size: 494 B |
BIN
Szmedi.RvKits/ScriptPad/resource/Images/Redo_16xMD.png
Normal file
|
After Width: | Height: | Size: 419 B |
BIN
Szmedi.RvKits/ScriptPad/resource/Images/Redo_grey_16x.png
Normal file
|
After Width: | Height: | Size: 469 B |
BIN
Szmedi.RvKits/ScriptPad/resource/Images/Redo_grey_16xMD.png
Normal file
|
After Width: | Height: | Size: 400 B |
BIN
Szmedi.RvKits/ScriptPad/resource/Images/Reference_16x.png
Normal file
|
After Width: | Height: | Size: 141 B |
BIN
Szmedi.RvKits/ScriptPad/resource/Images/Run_16x.png
Normal file
|
After Width: | Height: | Size: 233 B |
BIN
Szmedi.RvKits/ScriptPad/resource/Images/Run_16xMD.png
Normal file
|
After Width: | Height: | Size: 310 B |
BIN
Szmedi.RvKits/ScriptPad/resource/Images/Save_16x.png
Normal file
|
After Width: | Height: | Size: 237 B |
BIN
Szmedi.RvKits/ScriptPad/resource/Images/Save_grey_16x.png
Normal file
|
After Width: | Height: | Size: 216 B |
BIN
Szmedi.RvKits/ScriptPad/resource/Images/Search_16x.png
Normal file
|
After Width: | Height: | Size: 529 B |
BIN
Szmedi.RvKits/ScriptPad/resource/Images/Structure_16x.png
Normal file
|
After Width: | Height: | Size: 167 B |
BIN
Szmedi.RvKits/ScriptPad/resource/Images/UncommentCode_16x.png
Normal file
|
After Width: | Height: | Size: 380 B |
BIN
Szmedi.RvKits/ScriptPad/resource/Images/Undo_16x.png
Normal file
|
After Width: | Height: | Size: 432 B |
BIN
Szmedi.RvKits/ScriptPad/resource/Images/Undo_16xMD.png
Normal file
|
After Width: | Height: | Size: 412 B |
BIN
Szmedi.RvKits/ScriptPad/resource/Images/Undo_grey_16x.png
Normal file
|
After Width: | Height: | Size: 407 B |
51
Szmedi.RvKits/ScriptPad/revit/ExEventHandle.cs
Normal file
@@ -0,0 +1,51 @@
|
||||
using Autodesk.Revit.DB;
|
||||
using Autodesk.Revit.UI;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
|
||||
namespace RevitTools.Utils.Revit
|
||||
{
|
||||
/// <summary>
|
||||
/// 外部事件处理程序
|
||||
/// </summary>
|
||||
public class ExEventHandle : IExternalEventHandler
|
||||
{
|
||||
/// <summary>
|
||||
/// 创建具有指定名字的外部事件处理程序
|
||||
/// </summary>
|
||||
/// <param name="name"></param>
|
||||
public ExEventHandle(string name)
|
||||
{
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
private readonly string name;
|
||||
|
||||
/// <summary>
|
||||
/// 工作项队列
|
||||
/// </summary>
|
||||
internal readonly ConcurrentQueue<ExEventWorkItem> ExEventQueue = new ConcurrentQueue<ExEventWorkItem>();
|
||||
|
||||
/// <summary>
|
||||
/// 执行外部事件函数
|
||||
/// </summary>
|
||||
/// <param name="app"></param>
|
||||
public void Execute(UIApplication app)
|
||||
{
|
||||
while (ExEventQueue.TryDequeue(out ExEventWorkItem workItem))
|
||||
{
|
||||
workItem.InvokeAction(app);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取字符串标识
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public string GetName()
|
||||
{
|
||||
return name;
|
||||
}
|
||||
}
|
||||
}
|
||||
78
Szmedi.RvKits/ScriptPad/revit/ExEventUtils.cs
Normal file
@@ -0,0 +1,78 @@
|
||||
using Autodesk.Revit.UI;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace RevitTools.Utils.Revit
|
||||
{
|
||||
/// <summary>
|
||||
/// 外部事件封装类
|
||||
/// </summary>
|
||||
public class ExEventUtils
|
||||
{
|
||||
/// <summary>
|
||||
/// 创建外部事件
|
||||
/// </summary>
|
||||
/// <param name="handleName"></param>
|
||||
private ExEventUtils(string handleName)
|
||||
{
|
||||
this.Handle = new ExEventHandle(handleName);
|
||||
this.ExEvent = ExternalEvent.Create(this.Handle);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 外部事件实例
|
||||
/// </summary>
|
||||
private readonly ExternalEvent ExEvent;
|
||||
|
||||
/// <summary>
|
||||
/// 外部事件处理程序
|
||||
/// </summary>
|
||||
private readonly ExEventHandle Handle;
|
||||
|
||||
/// <summary>
|
||||
/// 在外部事件中执行函数, 可以等待函数执行完成
|
||||
/// </summary>
|
||||
/// <param name="action">要执行的函数</param>
|
||||
/// <param name="transactionGroupName">事务组名</param>
|
||||
/// <returns></returns>
|
||||
public async Task InvokeAsync(Action<UIApplication> action)
|
||||
{
|
||||
var workItem = new ExEventWorkItem(action);
|
||||
Handle.ExEventQueue.Enqueue(workItem);
|
||||
ExEvent.Raise();
|
||||
await workItem.CompletionSource.Task;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 在外部事件中执行函数, 并且可以等待函数执行完成
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="func">要执行的函数</param>
|
||||
/// <returns></returns>
|
||||
public async Task<T> InvokeAsync<T>(Func<UIApplication, T> func)
|
||||
{
|
||||
object func1(UIApplication app)
|
||||
{
|
||||
return func(app);
|
||||
}
|
||||
var workItem = new ExEventWorkItem(func1);
|
||||
Handle.ExEventQueue.Enqueue(workItem);
|
||||
ExEvent.Raise();
|
||||
var result = await workItem.CompletionSource.Task;
|
||||
return (T)result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取或创建外部事件处理实例
|
||||
/// </summary>
|
||||
/// <param name="name">外部事件处理程序名称</param>
|
||||
/// <returns></returns>
|
||||
public static ExEventUtils GetOrCreate(string name = "ScriptPadExEventHandle")
|
||||
{
|
||||
return EventUtils.GetOrAdd(name, new ExEventUtils(name));
|
||||
}
|
||||
|
||||
private static readonly ConcurrentDictionary<string, ExEventUtils> EventUtils = new ConcurrentDictionary<string, ExEventUtils>();
|
||||
}
|
||||
}
|
||||
64
Szmedi.RvKits/ScriptPad/revit/ExEventWorkItem.cs
Normal file
@@ -0,0 +1,64 @@
|
||||
using Autodesk.Revit.UI;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace RevitTools.Utils.Revit
|
||||
{
|
||||
/// <summary>
|
||||
/// 外部事件工作项
|
||||
/// </summary>
|
||||
internal class ExEventWorkItem
|
||||
{
|
||||
/// <summary>
|
||||
/// 异步阻塞信号
|
||||
/// </summary>
|
||||
public TaskCompletionSource<object> CompletionSource { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// 在外部事件中执行的, 带有一个返回值的函数
|
||||
/// </summary>
|
||||
private Action<UIApplication> Action { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 在外部事件中执行的, 带有一个返回值的函数
|
||||
/// </summary>
|
||||
private Func<UIApplication, object> Func { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 执行外部事件函数
|
||||
/// </summary>
|
||||
/// <param name="application"></param>
|
||||
public void InvokeAction(UIApplication application)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (Action != null)
|
||||
{
|
||||
Action.Invoke(application);
|
||||
this.CompletionSource.TrySetResult(null);
|
||||
}
|
||||
else
|
||||
{
|
||||
var result = Func.Invoke(application);
|
||||
this.CompletionSource.TrySetResult(result);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
this.CompletionSource.TrySetException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
public ExEventWorkItem(Action<UIApplication> action)
|
||||
{
|
||||
this.CompletionSource = new TaskCompletionSource<object>();
|
||||
this.Action = action;
|
||||
}
|
||||
|
||||
public ExEventWorkItem(Func<UIApplication, object> action)
|
||||
{
|
||||
this.CompletionSource = new TaskCompletionSource<object>();
|
||||
this.Func = action;
|
||||
}
|
||||
}
|
||||
}
|
||||
70
Szmedi.RvKits/ScriptPad/roslyn/ErrorListItem.cs
Normal file
@@ -0,0 +1,70 @@
|
||||
using Microsoft.CodeAnalysis;
|
||||
|
||||
namespace ScriptPad.Roslyn
|
||||
{
|
||||
/// <summary>
|
||||
/// 错误项
|
||||
/// </summary>
|
||||
public class ErrorListItem
|
||||
{
|
||||
public ErrorListItem(ErrorSeverity errorSeverity, string description, int startLine, int startColumn, int endLine, int endColumn)
|
||||
{
|
||||
ErrorSeverity = errorSeverity;
|
||||
Description = description;
|
||||
StartLine = startLine;
|
||||
StartColumn = startColumn;
|
||||
EndLine = endLine;
|
||||
EndColumn = endColumn;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 严重性
|
||||
/// </summary>
|
||||
public ErrorSeverity ErrorSeverity { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 描述
|
||||
/// </summary>
|
||||
public string Description { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 起始行
|
||||
/// </summary>
|
||||
public int StartLine { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 起始列
|
||||
/// </summary>
|
||||
public int StartColumn { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 结束行
|
||||
/// </summary>
|
||||
public int EndLine { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 结束列
|
||||
/// </summary>
|
||||
public int EndColumn { get; }
|
||||
|
||||
public static ErrorListItem CreateErrorListItem(Diagnostic diagnostic)
|
||||
{
|
||||
var mappedSpan = diagnostic.Location.GetMappedLineSpan();
|
||||
ErrorSeverity errorSeverity;
|
||||
if (diagnostic.Severity == DiagnosticSeverity.Error)
|
||||
{
|
||||
errorSeverity = ErrorSeverity.Error;
|
||||
}
|
||||
else if (diagnostic.Severity == DiagnosticSeverity.Warning)
|
||||
{
|
||||
errorSeverity = ErrorSeverity.Warning;
|
||||
}
|
||||
else
|
||||
{
|
||||
errorSeverity = ErrorSeverity.Info;
|
||||
}
|
||||
return new ErrorListItem(errorSeverity, diagnostic.GetMessage(), mappedSpan.Span.Start.Line, mappedSpan.Span.Start.Character,
|
||||
mappedSpan.Span.End.Line, mappedSpan.Span.End.Character);
|
||||
}
|
||||
}
|
||||
}
|
||||
23
Szmedi.RvKits/ScriptPad/roslyn/ErrorSeverity.cs
Normal file
@@ -0,0 +1,23 @@
|
||||
namespace ScriptPad.Roslyn
|
||||
{
|
||||
/// <summary>
|
||||
/// 错误严重性
|
||||
/// </summary>
|
||||
public enum ErrorSeverity
|
||||
{
|
||||
/// <summary>
|
||||
/// 信息
|
||||
/// </summary>
|
||||
Info,
|
||||
|
||||
/// <summary>
|
||||
/// 警告
|
||||
/// </summary>
|
||||
Warning,
|
||||
|
||||
/// <summary>
|
||||
/// 错误
|
||||
/// </summary>
|
||||
Error
|
||||
}
|
||||
}
|
||||
24
Szmedi.RvKits/ScriptPad/roslyn/ScriptRunner.cs
Normal file
@@ -0,0 +1,24 @@
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.CodeAnalysis.Scripting;
|
||||
using Microsoft.CodeAnalysis.CSharp.Scripting;
|
||||
|
||||
namespace ScriptPad.Roslyn
|
||||
{
|
||||
public class ScriptRunner
|
||||
{
|
||||
public static async Task Run(CsScript Script)
|
||||
{
|
||||
var options = ScriptOptions.Default;
|
||||
options = options.AddReferences(Script.GetReferences());
|
||||
options = options.AddReferences(ScriptGlobals.InitAssemblies);
|
||||
|
||||
var script = CSharpScript.Create(await Script.GetScriptText(), options, globalsType: ScriptGlobals.GlobalObject.GetType());
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(ScriptGlobals.StartScript))
|
||||
script = script.ContinueWith(ScriptGlobals.StartScript, options);
|
||||
|
||||
await script.RunAsync(ScriptGlobals.GlobalObject);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
138
Szmedi.RvKits/ScriptPad/roslyn/ScriptingWorkspace.cs
Normal file
@@ -0,0 +1,138 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
using Microsoft.CodeAnalysis.Host;
|
||||
using System.Reflection;
|
||||
using Microsoft.CodeAnalysis.CSharp;
|
||||
using Microsoft.CodeAnalysis.Text;
|
||||
using System.Text;
|
||||
using Microsoft.CodeAnalysis.Host.Mef;
|
||||
using System.Composition.Hosting;
|
||||
|
||||
namespace ScriptPad.Roslyn
|
||||
{
|
||||
internal class ScriptingWorkspace : Workspace
|
||||
{
|
||||
private ScriptingWorkspace(HostServices hostServices) : base(hostServices, WorkspaceKind.Interactive)
|
||||
{
|
||||
}
|
||||
|
||||
public Document GetDocument(DocumentId id)
|
||||
{
|
||||
return CurrentSolution.GetDocument(id);
|
||||
}
|
||||
|
||||
public DocumentId AddProjectWithDocument(string documentFileName, string text)
|
||||
{
|
||||
var fileName = Path.GetFileName(documentFileName);
|
||||
var name = Path.GetFileNameWithoutExtension(documentFileName);
|
||||
|
||||
var projectId = ProjectId.CreateNewId();
|
||||
|
||||
var references = ScriptGlobals.InitAssemblies.Distinct().Select(CreateReference).ToList();
|
||||
|
||||
var projectInfo = ProjectInfo.Create(
|
||||
projectId,
|
||||
VersionStamp.Default,
|
||||
name,
|
||||
name,
|
||||
LanguageNames.CSharp,
|
||||
isSubmission: true,
|
||||
compilationOptions: new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary).WithScriptClassName(name),
|
||||
metadataReferences: references,
|
||||
parseOptions: new CSharpParseOptions(languageVersion: LanguageVersion.Latest));
|
||||
|
||||
OnProjectAdded(projectInfo);
|
||||
|
||||
var documentId = DocumentId.CreateNewId(projectId);
|
||||
var documentInfo = DocumentInfo.Create(
|
||||
documentId,
|
||||
fileName,
|
||||
sourceCodeKind: SourceCodeKind.Script,
|
||||
loader: TextLoader.From(TextAndVersion.Create(SourceText.From(text, Encoding.UTF8), VersionStamp.Create())));
|
||||
OnDocumentAdded(documentInfo);
|
||||
return documentId;
|
||||
}
|
||||
|
||||
public Project GetProject(DocumentId id)
|
||||
{
|
||||
return CurrentSolution.GetProject(id.ProjectId);
|
||||
}
|
||||
|
||||
public void RemoveProject(DocumentId id)
|
||||
{
|
||||
OnProjectRemoved(id.ProjectId);
|
||||
}
|
||||
|
||||
public void UpdateText(DocumentId documentId, string text)
|
||||
{
|
||||
OnDocumentTextChanged(documentId, SourceText.From(text, Encoding.UTF8), PreservationMode.PreserveValue);
|
||||
}
|
||||
|
||||
public void UpdateText(DocumentId documentid, SourceText text)
|
||||
{
|
||||
OnDocumentTextChanged(documentid, text, PreservationMode.PreserveValue);
|
||||
}
|
||||
|
||||
public Task<IReadOnlyList<Diagnostic>> GetDiagnosticsAsync(DocumentId documentId, CancellationToken cancellationToken)
|
||||
{
|
||||
return Task.Run(async () =>
|
||||
{
|
||||
var project = CurrentSolution.GetProject(documentId.ProjectId);
|
||||
var compilation = await project.GetCompilationAsync(cancellationToken);
|
||||
return (IReadOnlyList<Diagnostic>)compilation.GetDiagnostics(cancellationToken);
|
||||
}, cancellationToken);
|
||||
}
|
||||
|
||||
public override bool CanApplyChange(ApplyChangesKind feature)
|
||||
{
|
||||
return base.CanApplyChange(feature);
|
||||
}
|
||||
|
||||
private MetadataReference CreateReference(Assembly assembly)
|
||||
{
|
||||
return MetadataReference.CreateFromFile(assembly.Location);
|
||||
}
|
||||
|
||||
public void AddReference(string path, DocumentId id)
|
||||
{
|
||||
var references = GetReferences(id).OfType<PortableExecutableReference>();
|
||||
if (references.Any(p => p.FilePath == path))
|
||||
return;
|
||||
|
||||
PortableExecutableReference data = MetadataReference.CreateFromFile(path);
|
||||
var pid = GetDocument(id).Project.Id;
|
||||
OnMetadataReferenceAdded(pid, data);
|
||||
}
|
||||
|
||||
public void RemoveReference(string path, DocumentId id)
|
||||
{
|
||||
var project = GetDocument(id).Project;
|
||||
var data = project.MetadataReferences.FirstOrDefault(p => (p as PortableExecutableReference).FilePath == path);
|
||||
|
||||
OnMetadataReferenceRemoved(project.Id, data);
|
||||
}
|
||||
|
||||
public IEnumerable<MetadataReference> GetReferences(DocumentId id)
|
||||
{
|
||||
var project = GetDocument(id).Project;
|
||||
return project.MetadataReferences;
|
||||
}
|
||||
|
||||
private static Lazy<ScriptingWorkspace> instance = new Lazy<ScriptingWorkspace>(() =>
|
||||
{
|
||||
var compositionHost = new ContainerConfiguration().WithAssemblies(MefHostServices.DefaultAssemblies).CreateContainer();
|
||||
var hostService = MefHostServices.Create(compositionHost);
|
||||
return new ScriptingWorkspace(hostService);
|
||||
}, true);
|
||||
|
||||
public static ScriptingWorkspace GetInstance()
|
||||
{
|
||||
return instance.Value;
|
||||
}
|
||||
}
|
||||
}
|
||||
42
Szmedi.RvKits/ScriptPad/service/CompletionService.cs
Normal file
@@ -0,0 +1,42 @@
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.Completion;
|
||||
using Microsoft.CodeAnalysis.Text;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace ScriptPad.Service
|
||||
{
|
||||
public class ScriptCompletionService
|
||||
{
|
||||
public static async Task<IEnumerable<Editor.CodeCompletionData>> GetCompletionsAsync(DocumentId id, int position)
|
||||
{
|
||||
var document = Roslyn.ScriptingWorkspace.GetInstance().GetDocument(id);
|
||||
var completionService = CompletionService.GetService(document);
|
||||
var list = await completionService.GetCompletionsAsync(document, position);
|
||||
|
||||
if (list == null || !list.Items.Any())
|
||||
return new List<Editor.CodeCompletionData>();
|
||||
|
||||
return list.Items.Select(p => new Editor.CodeCompletionData(id, p));
|
||||
}
|
||||
|
||||
public static async Task<string> GetDescriptionAsync(DocumentId id, CompletionItem completionItem)
|
||||
{
|
||||
var document = Roslyn.ScriptingWorkspace.GetInstance().GetDocument(id);
|
||||
var completionService = CompletionService.GetService(document);
|
||||
var des = await completionService.GetDescriptionAsync(document, completionItem);
|
||||
return des.Text;
|
||||
}
|
||||
|
||||
public static bool IsTrigger(DocumentId id, SourceText text, int position, char? triggerChar)
|
||||
{
|
||||
var document = Roslyn.ScriptingWorkspace.GetInstance().GetDocument(id);
|
||||
var completionService = CompletionService.GetService(document);
|
||||
if(triggerChar != null)
|
||||
return completionService.ShouldTriggerCompletion(text, position, CompletionTrigger.CreateInsertionTrigger(triggerChar.Value));
|
||||
return completionService.ShouldTriggerCompletion(text, position, CompletionTrigger.Invoke);
|
||||
}
|
||||
}
|
||||
}
|
||||