Skip to content

Commit 66c1e25

Browse files
committed
Initial pass at loadExternalActionSource command
1 parent 611e7d8 commit 66c1e25

File tree

4 files changed

+94
-0
lines changed

4 files changed

+94
-0
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
1111
- Added support for variadic functions.
1212
- Added support for cancelling compilation.
1313
- To enable cancelling a compilation, supply a `CancellationToken` to your `CompilationJob` object. You can request that the compilation be cancelled by cancelling the token. For more information, see [Task Cancellation](https://learn.microsoft.com/en-us/dotnet/standard/parallel-programming/task-cancellation).
14+
- Added a new language server command `yarnspinner.loadExternalActionSource` which can be used to programatically load command / function definitions from an external source (eg. a vscode extension that parses yarn commands/functions from a non-C# language).
1415

1516
### Changed
1617

YarnSpinner.LanguageServer/src/Server/Commands/Commands.cs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,4 +73,11 @@ public static class Commands
7373
/// The command to get all projects in the current workspace.
7474
/// </summary>
7575
public const string ListProjects = "yarnspinner.listProjects";
76+
77+
/// <summary>
78+
/// The command to import function and command definitions from external sources.
79+
/// </summary>
80+
/// <param name="sourceKey">The unique key identifying the external action source.</param>
81+
/// <param name="sourceContent">A string containing JSON with the same schema as the .ysls.json file.</param>
82+
public const string LoadExternalActionSource = "yarnspinner.loadExternalActionSource";
7683
}

YarnSpinner.LanguageServer/src/Server/Workspace/Workspace.cs

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,11 @@ internal IEnumerable<Project> GetProjectsForUri(DocumentUri uri)
5959
/// </summary>
6060
private HashSet<Action> workspaceActions = new HashSet<Action>();
6161

62+
/// <summary>
63+
/// The collection of actions loaded using the loadExternalActionSource command.
64+
/// </summary>
65+
private Dictionary<string, HashSet<Action>> externalActions = new Dictionary<string, HashSet<Action>>();
66+
6267
Configuration IConfigurationSource.Configuration => this.Configuration;
6368

6469
internal void ReloadWorkspace(CancellationToken cancellationToken)
@@ -73,6 +78,15 @@ internal void ReloadWorkspace(CancellationToken cancellationToken)
7378
this.workspaceActions = new HashSet<Action>();
7479
}
7580

81+
// Merge in the actions from external sources
82+
foreach (var externalSource in this.externalActions)
83+
{
84+
foreach (var action in externalSource.Value)
85+
{
86+
this.workspaceActions.Add(action);
87+
}
88+
}
89+
7690
// Find all actions built in to this DLL
7791
try
7892
{
@@ -229,6 +243,21 @@ internal static IEnumerable<Action> GetPredefinedActions()
229243
return predefinedActions;
230244
}
231245

246+
/// <summary>
247+
/// Imports an external action source into the workspace.
248+
/// </summary>
249+
/// <remarks>
250+
/// Existing actions with the same source key will be deleted / replaced.
251+
/// This will not automatically relaod the workspace,
252+
/// so it is up to the caller to call <see cref="ReloadWorkspace(CancellationToken)"/> after importing
253+
/// the external actions to ensure that the workspace is up to date with the new actions.
254+
/// </remarks>
255+
internal void ImportExternalActionSource(string sourceKey, HashSet<Action> actionsSet)
256+
{
257+
this.externalActions.Remove(sourceKey);
258+
this.externalActions.Add(sourceKey, actionsSet);
259+
}
260+
232261
private IEnumerable<Action> FindWorkspaceActions(string root)
233262
{
234263
var csharpWorkspaceFiles = System.IO.Directory.EnumerateFiles(root, "*.cs", System.IO.SearchOption.AllDirectories);

YarnSpinner.LanguageServer/src/Server/YarnLanguageServer.cs

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,14 @@
66
using System.Threading.Tasks;
77
using Google.Protobuf;
88
using Microsoft.Extensions.DependencyInjection;
9+
using Newtonsoft.Json;
910
using Newtonsoft.Json.Linq;
1011
using OmniSharp.Extensions.LanguageServer.Protocol.Models;
1112
using OmniSharp.Extensions.LanguageServer.Protocol.Server;
1213
using OmniSharp.Extensions.LanguageServer.Protocol.Window;
1314
using OmniSharp.Extensions.LanguageServer.Protocol.Workspace;
1415
using OmniSharp.Extensions.LanguageServer.Server;
16+
using static YarnLanguageServer.JsonConfigFile;
1517

1618
[assembly: System.Runtime.CompilerServices.InternalsVisibleTo("YarnLanguageServer.Tests")]
1719

@@ -182,6 +184,15 @@ public static LanguageServerOptions ConfigureOptions(LanguageServerOptions optio
182184
}
183185
);
184186

187+
// Register Load External Action Source command
188+
options.OnExecuteCommand<bool>(
189+
(commandParams, cancellationToken) => LoadExternalActionSourceAsync(workspace, commandParams, cancellationToken),
190+
(_, _) => new ExecuteCommandRegistrationOptions
191+
{
192+
Commands = new[] { Commands.LoadExternalActionSource },
193+
}
194+
);
195+
185196
return options;
186197
}
187198

@@ -829,5 +840,51 @@ private static Task<Container<ProjectInfo>> ListProjectsAsync(Workspace workspac
829840
});
830841
return Task.FromResult(Container.From(info));
831842
}
843+
844+
private static Task<bool> LoadExternalActionSourceAsync(Workspace workspace, ExecuteCommandParams<bool> commandParams, CancellationToken cancellationToken)
845+
{
846+
if (commandParams.Arguments == null || commandParams.Arguments.Count < 2)
847+
{
848+
workspace.ShowMessage($"{Commands.LoadExternalActionSource} expects at least two arguments: a source key and a json of actions (same format as ysls.json)", MessageType.Error);
849+
return Task.FromResult(false);
850+
}
851+
852+
var externalActionSourceName = commandParams.Arguments[0].ToString();
853+
var externalActionContent = commandParams.Arguments[1].ToString();
854+
855+
var parsedActions = JsonConvert.DeserializeObject<JsonConfigFormat>(externalActionContent);
856+
if (parsedActions == null)
857+
{
858+
workspace.ShowMessage($"Failed to parse external action source JSON for {externalActionSourceName}", MessageType.Error);
859+
return Task.FromResult(false);
860+
}
861+
862+
var actionsToImport = new HashSet<Action>();
863+
foreach (var definition in parsedActions.Functions)
864+
{
865+
Action action = definition.ToAction();
866+
action.IsBuiltIn = false;
867+
action.Type = ActionType.Function;
868+
if (!actionsToImport.Add(action)) {
869+
workspace.ShowMessage($"Duplicate action found for function: {externalActionSourceName}", MessageType.Error);
870+
return Task.FromResult(false);
871+
}
872+
}
873+
874+
foreach (var definition in parsedActions.Commands)
875+
{
876+
Action action = definition.ToAction();
877+
action.IsBuiltIn = false;
878+
action.Type = ActionType.Command;
879+
if (!actionsToImport.Add(action)) {
880+
workspace.ShowMessage($"Duplicate action found for command: {externalActionSourceName}", MessageType.Error);
881+
return Task.FromResult(false);
882+
}
883+
}
884+
885+
workspace.ImportExternalActionSource(externalActionSourceName, actionsToImport);
886+
workspace.ReloadWorkspace(cancellationToken);
887+
return Task.FromResult(true);
888+
}
832889
}
833890
}

0 commit comments

Comments
 (0)