Skip to content

Commit

Permalink
Merge pull request #3010 from VsVim/dev/nosami/csharp-scripting
Browse files Browse the repository at this point in the history
Adds C# scripting capability to VSMac
  • Loading branch information
nosami committed Sep 11, 2022
2 parents bf8092b + 97344c0 commit 94422a0
Show file tree
Hide file tree
Showing 7 changed files with 171 additions and 20 deletions.
16 changes: 13 additions & 3 deletions Documentation/CSharp scripting.md
Expand Up @@ -2,11 +2,11 @@ C# Scripting
===

VsVim 2.7.0 adds the feature to run C# scripts.
This feature is supported in Visual Studio 2019 and later.
This feature is supported in Visual Studio 2019 and later as well as Visual Studio for Mac.

## Execution method

Create the `VsVimScripts` folder in the user profile folder.
Create the `vsvimscripts` folder in the user profile folder.
Place the script file directly under it.
The extension of the script file is `csx`.

Expand All @@ -27,6 +27,7 @@ It is assumed to be used for debugging.

create the following script file.

Windows version
```csharp
//Hello.csx
Expand All @@ -35,7 +36,16 @@ using System.Windows;
MessageBox.Show("Hello, World!");

```
Place this file in the `VsVimScripts` folder.

or for Mac

```csharp
//Hello.csx
MonoDevelop.Ide.MessageService.ShowMessage("Hello, World!");
```

Place this file in the `vsvimscripts` folder.
Type `csx hello` in command mode.
If the message box is displayed, it is successful.

Expand Down
118 changes: 118 additions & 0 deletions Src/VimMac/CSharpScript/CSharpScriptExecutor.cs
@@ -0,0 +1,118 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.IO;
using System.Reflection;
using System.Threading.Tasks;
using Vim.Interpreter;

namespace Vim.VisualStudio.Implementation.CSharpScript
{
using Microsoft.CodeAnalysis.CSharp.Scripting;
using Microsoft.CodeAnalysis.Scripting;
using CSharpScript = Microsoft.CodeAnalysis.CSharp.Scripting.CSharpScript;

[Export(typeof(ICSharpScriptExecutor))]
internal sealed partial class CSharpScriptExecutor : ICSharpScriptExecutor
{
private const string ScriptFolder = "vsvimscripts";
private Dictionary<string, Script<object>> _scripts = new Dictionary<string, Script<object>>(StringComparer.OrdinalIgnoreCase);
private ScriptOptions _scriptOptions = null;

[ImportingConstructor]
public CSharpScriptExecutor()
{

}

private async Task ExecuteAsync(IVimBuffer vimBuffer, CallInfo callInfo, bool createEachTime)
{
try
{
Script<object> script;
if (!TryGetScript(vimBuffer, callInfo.Name, createEachTime, out script))
return;

var globals = new CSharpScriptGlobals(callInfo, vimBuffer);
var scriptState = await script.RunAsync(globals);
}
catch (CompilationErrorException ex)
{
if (_scripts.ContainsKey(callInfo.Name))
_scripts.Remove(callInfo.Name);

vimBuffer.VimBufferData.StatusUtil.OnError(string.Join(Environment.NewLine, ex.Diagnostics));
}
catch (Exception ex)
{
vimBuffer.VimBufferData.StatusUtil.OnError(ex.Message);
}
}

private static ScriptOptions GetScriptOptions(string scriptPath)
{
var ssr = ScriptSourceResolver.Default.WithBaseDirectory(scriptPath);
var baseDirectory = AppDomain.CurrentDomain.BaseDirectory;

var searchPaths = new string[]
{
baseDirectory,
Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location)
};

var smr = ScriptMetadataResolver.Default
.WithBaseDirectory(scriptPath)
.WithSearchPaths(searchPaths);

var asm = new Assembly[]
{
typeof(MonoDevelop.Core.Runtime).Assembly,
typeof(Vim.IVim).Assembly, // VimCore.dll
typeof(Vim.Mac.VimCocoaHost).Assembly // Vim.VisualStudio.Shared.dll
};

var so = ScriptOptions.Default
.WithSourceResolver(ssr)
.WithMetadataResolver(smr)
.WithReferences(asm);

return so;
}

private bool TryGetScript(IVimBuffer vimBuffer, string scriptName, bool createEachTime, out Script<object> script)
{
if (!createEachTime && _scripts.TryGetValue(scriptName, out script))
return true;

string scriptPath = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile);
scriptPath = Path.Combine(scriptPath, ScriptFolder);

string scriptFilePath = Path.Combine(scriptPath, $"{scriptName}.csx");

if (!File.Exists(scriptFilePath))
{
vimBuffer.VimBufferData.StatusUtil.OnError("script file not found.");
script = null;
return false;
}

if (_scriptOptions == null)
_scriptOptions = GetScriptOptions(scriptPath);

script = CSharpScript.Create(File.ReadAllText(scriptFilePath), _scriptOptions, typeof(CSharpScriptGlobals));
_scripts[scriptName] = script;
return true;
}

#region ICSharpScriptExecutor

void ICSharpScriptExecutor.Execute(IVimBuffer vimBuffer, CallInfo callInfo, bool createEachTime)
{
var task = ExecuteAsync(vimBuffer, callInfo, createEachTime);
VimTrace.TraceInfo("CSharptScript:Execute {0}", callInfo.Name);
}

#endregion

}
}
24 changes: 24 additions & 0 deletions Src/VimMac/CSharpScript/CSharpScriptGlobals.cs
@@ -0,0 +1,24 @@
using Vim.Interpreter;

namespace Vim.VisualStudio.Implementation.CSharpScript
{
public class CSharpScriptGlobals
{
public string Name { get; } = string.Empty;
public string Arguments { get; } = string.Empty;
public LineRangeSpecifier LineRange { get; }
public bool IsScriptLocal { get; } = false;
public IVim Vim { get; } = null;
public IVimBuffer VimBuffer { get; } = null;

public CSharpScriptGlobals(CallInfo callInfo, IVimBuffer vimBuffer)
{
Name = callInfo.Name;
Arguments = callInfo.Arguments;
LineRange = callInfo.LineRange;
IsScriptLocal = callInfo.IsScriptLocal;
Vim = vimBuffer.Vim;
VimBuffer = vimBuffer;
}
}
}
9 changes: 9 additions & 0 deletions Src/VimMac/CSharpScript/ICSharpScriptExecutor.cs
@@ -0,0 +1,9 @@
using Vim.Interpreter;

namespace Vim.VisualStudio
{
public interface ICSharpScriptExecutor
{
void Execute(IVimBuffer vimBuffer, CallInfo callInfo, bool createEachTime);
}
}
2 changes: 1 addition & 1 deletion Src/VimMac/Properties/AddinInfo.cs
Expand Up @@ -5,7 +5,7 @@
[assembly: Addin(
"VsVim",
Namespace = "Vim.Mac",
Version = "2.8.0.17"
Version = "2.8.0.18"
)]

[assembly: AddinName("VsVim")]
Expand Down
9 changes: 5 additions & 4 deletions Src/VimMac/VimHost.cs
Expand Up @@ -11,12 +11,10 @@
using Microsoft.VisualStudio.Text.Editor;
using Microsoft.VisualStudio.Text.Formatting;
using Microsoft.VisualStudio.Utilities;
using MonoDevelop.Components.Commands;
using MonoDevelop.Core;
using MonoDevelop.Ide;
using MonoDevelop.Ide.CodeFormatting;
using MonoDevelop.Ide.Commands;
using MonoDevelop.Ide.FindInFiles;
using MonoDevelop.Ide.Gui;
using MonoDevelop.Projects;
using Vim.Extensions;
Expand All @@ -36,6 +34,7 @@ internal sealed class VimCocoaHost : IVimHost
private readonly ICocoaTextEditorFactoryService _textEditorFactoryService;
private readonly ISmartIndentationService _smartIndentationService;
private readonly IExtensionAdapterBroker _extensionAdapterBroker;
private readonly ICSharpScriptExecutor _scriptExecutor;
private IVim _vim;

internal const string CommandNameGoToDefinition = "MonoDevelop.Refactoring.RefactoryCommands.GotoDeclaration";
Expand All @@ -45,14 +44,16 @@ internal sealed class VimCocoaHost : IVimHost
ITextBufferFactoryService textBufferFactoryService,
ICocoaTextEditorFactoryService textEditorFactoryService,
ISmartIndentationService smartIndentationService,
IExtensionAdapterBroker extensionAdapterBroker)
IExtensionAdapterBroker extensionAdapterBroker,
ICSharpScriptExecutor scriptExecutor)
{
VimTrace.TraceSwitch.Level = System.Diagnostics.TraceLevel.Verbose;
Console.WriteLine("Loaded");
_textBufferFactoryService = textBufferFactoryService;
_textEditorFactoryService = textEditorFactoryService;
_smartIndentationService = smartIndentationService;
_extensionAdapterBroker = extensionAdapterBroker;
_scriptExecutor = scriptExecutor;
}

public bool AutoSynchronizeSettings => false;
Expand Down Expand Up @@ -664,7 +665,7 @@ public RunCommandResults RunCommand(string workingDirectory, string command, str

public void RunCSharpScript(IVimBuffer vimBuffer, CallInfo callInfo, bool createEachTime)
{
throw new NotImplementedException();
_scriptExecutor.Execute(vimBuffer, callInfo, createEachTime);
}

public void RunHostCommand(ITextView textView, string commandName, string argument)
Expand Down
13 changes: 1 addition & 12 deletions Src/VimMac/VimMac.csproj
Expand Up @@ -12,22 +12,11 @@
<ItemGroup>
<ProjectReference Include="..\VimCore\VimCore.fsproj" />
<PackageReference Include="Microsoft.VisualStudioMac.Sdk" Version="17.0.0" />
<PackageReference Include="Microsoft.CodeAnalysis.Scripting" Version="4.3.0.0" />
</ItemGroup>
<ItemGroup>
<AddinReference Include="MonoDevelop.TextEditor" />
</ItemGroup>
<ItemGroup>
<Folder Include="Properties\" />
<Folder Include="Resources\" />
<Folder Include="FPFExtensions\" />
<Folder Include="InlineRename\" />
<Folder Include="RelativeLineNumbers\" />
<Folder Include="RelativeLineNumbers\Util\" />
</ItemGroup>
<ItemGroup>
<None Remove="Resources\beep.wav" />
<None Remove="Resources\KeyBindingSchemeVim.xml" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="Resources\beep.wav" />
<EmbeddedResource Include="Resources\KeyBindingSchemeVim.xml" />
Expand Down

0 comments on commit 94422a0

Please sign in to comment.