diff --git a/AnyText/AnyText.Core/CodeActionInfo.cs b/AnyText/AnyText.Core/CodeActionInfo.cs
new file mode 100644
index 00000000..2caee1e2
--- /dev/null
+++ b/AnyText/AnyText.Core/CodeActionInfo.cs
@@ -0,0 +1,75 @@
+using System;
+using System.Collections.Generic;
+using NMF.AnyText.Rules;
+using NMF.AnyText.Workspace;
+
+namespace NMF.AnyText
+{
+ ///
+ /// Represents the information about a code action.
+ ///
+ public class CodeActionInfo
+ {
+ ///
+ /// RuleApplication of the Action
+ ///
+ public RuleApplication RuleApplication { get; set; }
+ ///
+ /// The title is typically displayed in the UI to describe the action.
+ ///
+ public string Title { get; set; }
+
+ ///
+ /// Kind of the code action.
+ /// Possible values:
+ /// - "quickfix"
+ /// - "refactor"
+ /// - "refactor.extract"
+ /// - "refactor.inline"
+ /// - "refactor.rewrite"
+ /// - "source"
+ /// - "source.organizeImports"
+ ///
+ public string Kind { get; set; }
+
+ ///
+ /// This array holds diagnostics for which this action is relevant. If no diagnostics are set, the action may apply generally.
+ ///
+ public string[] Diagnostics { get; set; }
+
+ ///
+ /// A value of true indicates that the code action is preferred; otherwise, false or null if there's no preference.
+ ///
+ public bool IsPreferred { get; set; }
+
+ ///
+ /// This is the text that describes the command to execute, which can be shown to the user.
+ ///
+ public string CommandTitle { get; set; }
+
+ ///
+ /// The command is the identifier or name of the action to execute when the user selects it.
+ ///
+ public string CommandIdentifier { get; set; }
+
+ ///
+ /// These are the parameters passed to the command when it is executed.
+ ///
+ public Dictionary Arguments { get; set; }
+
+ ///
+ /// Identifies the Diagnostic that this Action fixes
+ ///
+ public string DiagnosticIdentifier { get; set; }
+
+ ///
+ /// Defines the how the WorkspaceEdit Object of this CodeAction is created
+ ///
+ public Func WorkspaceEdit { get; set; }
+
+ ///
+ /// The actual execution of this CodeAction
+ ///
+ public Action Action { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/AnyText/AnyText.Core/CodeLensInfo.cs b/AnyText/AnyText.Core/CodeLensInfo.cs
new file mode 100644
index 00000000..c7fbe351
--- /dev/null
+++ b/AnyText/AnyText.Core/CodeLensInfo.cs
@@ -0,0 +1,41 @@
+using System;
+using System.Collections.Generic;
+using NMF.AnyText.Rules;
+
+namespace NMF.AnyText
+{
+ ///
+ /// Represents a CodeLens item used for a Language Server Protocol (LSP) server.
+ /// CodeLens provides information or actions associated with specific locations in a text document.
+ ///
+ public class CodeLensInfo
+ {
+ ///
+ /// RuleApplication of the Lens
+ ///
+ public RuleApplication RuleApplication {get;set;}
+ ///
+ /// Gets or sets the title of the CodeLens item, typically a label displayed in the editor.
+ ///
+ public string Title { get; set; }
+
+ ///
+ /// Gets or sets the identifier for the command to be executed when the CodeLens is activated.
+ ///
+ public string CommandIdentifier { get; set; }
+
+ ///
+ /// Gets or sets the dictionary of arguments to be passed along with the command when invoked.
+ ///
+ public Dictionary Arguments { get; set; }
+
+ ///
+ /// Gets or sets additional data associated with this CodeLens, which can be used for custom functionality.
+ ///
+ public object Data { get; set; }
+ ///
+ /// The actual execution of this CodeLens
+ ///
+ public Action Action { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/AnyText/AnyText.Core/ExecuteCommandArguments.cs b/AnyText/AnyText.Core/ExecuteCommandArguments.cs
new file mode 100644
index 00000000..8ec4f226
--- /dev/null
+++ b/AnyText/AnyText.Core/ExecuteCommandArguments.cs
@@ -0,0 +1,41 @@
+using System.Collections.Generic;
+using NMF.AnyText.Rules;
+
+namespace NMF.AnyText
+{
+ ///
+ /// Represents the arguments for executing a command on a document.
+ ///
+ public class ExecuteCommandArguments
+ {
+ ///
+ /// RuleApplication of the Action
+ ///
+ public RuleApplication RuleApplication { get; set; }
+
+ ///
+ /// ParseContext of the Document
+ ///
+ public ParseContext Context { get; set; }
+
+ ///
+ /// URI of the document.
+ ///
+ public string DocumentUri { get; set; }
+
+ ///
+ /// Starting position of the Range.
+ ///
+ public ParsePosition Start { get; set; }
+
+ ///
+ /// Ending position of the Range.
+ ///
+ public ParsePosition End { get; set; }
+
+ ///
+ /// Additional options for the command execution.
+ ///
+ public Dictionary OtherOptions { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/AnyText/AnyText.Core/Grammars/Grammar.cs b/AnyText/AnyText.Core/Grammars/Grammar.cs
index 0755638c..c7f54fc3 100644
--- a/AnyText/AnyText.Core/Grammars/Grammar.cs
+++ b/AnyText/AnyText.Core/Grammars/Grammar.cs
@@ -56,6 +56,9 @@ public void Initialize()
foreach (var rule in allRules)
{
rule.Initialize(_context);
+
+ AddActionsFromRule(rule);
+
if (rule.TokenType != null)
{
CalculateTokenIndices(tokenTypes, tokenModifiers, rule, out int tokenTypeIndex, out int tokenModifierIndex);
@@ -73,6 +76,18 @@ public void Initialize()
}
}
+ private void AddActionsFromRule(Rule rule)
+ {
+ if (rule.SupportedCodeActions.Any())
+ foreach (var actionInfo in rule.SupportedCodeActions)
+ if (!string.IsNullOrEmpty(actionInfo.CommandIdentifier))
+ ExecutableActions.TryAdd(actionInfo.CommandIdentifier, actionInfo.Action);
+
+ if (rule.SupportedCodeLenses.Any())
+ foreach (var lensInfo in rule.SupportedCodeLenses)
+ ExecutableActions.TryAdd(lensInfo.CommandIdentifier, lensInfo.Action);
+ }
+
///
/// Gets an array of comment rules used by this grammar
///
@@ -177,5 +192,20 @@ public Rule Root
/// a context to resolve the root rule
/// the root rule for this grammar
protected abstract Rule GetRootRule(GrammarContext context);
+
+ ///
+ /// Dictionary of executable actions.
+ /// The key is the action identifier, and the value is the action executor.
+ ///
+ private Dictionary> ExecutableActions { get; } = new ();
+
+ ///
+ /// Retrieves the dictionary of executable actions as a read-only dictionary.
+ ///
+ /// A read-only view of the dictionary.
+ public IReadOnlyDictionary> GetExecutableActions()
+ {
+ return ExecutableActions;
+ }
}
}
diff --git a/AnyText/AnyText.Core/Parser.CodeAction.cs b/AnyText/AnyText.Core/Parser.CodeAction.cs
new file mode 100644
index 00000000..ff8b51bd
--- /dev/null
+++ b/AnyText/AnyText.Core/Parser.CodeAction.cs
@@ -0,0 +1,70 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using NMF.AnyText.Rules;
+
+namespace NMF.AnyText
+{
+ public partial class Parser
+ {
+ ///
+ /// Retrieves code action information within a specified range of parse positions.
+ ///
+ /// The starting position of the range.
+ /// The ending position of the range.
+ /// An optional predicate to filter rule applications.
+ /// A collection of objects representing available code actions.
+ public IEnumerable GetCodeActionInfo(ParsePosition start, ParsePosition end,
+ Predicate predicate = null)
+ {
+ predicate ??= _ => true;
+ var codeActionInfos = new List();
+
+ var ruleApp = Context.Matcher.GetRuleApplicationsAt(start)
+ .FirstOrDefault(r => r.Rule.IsLiteral);
+
+ if (ruleApp == null) return codeActionInfos.ToArray();
+
+ while (!(ruleApp.CurrentPosition <= start &&
+ ruleApp.CurrentPosition + ruleApp.Length >= end))
+ {
+ ruleApp = ruleApp.Parent;
+ if (ruleApp == null)
+ return codeActionInfos;
+ }
+
+ CollectCodeActionsWithRuleApplication(ruleApp, predicate, codeActionInfos);
+
+ var parent = ruleApp.Parent;
+ while (parent != null && parent.Length == ruleApp.Length)
+ {
+ CollectCodeActionsWithRuleApplication(parent, predicate, codeActionInfos);
+ parent = parent.Parent;
+
+ }
+
+
+ return codeActionInfos;
+ }
+
+ private static void CollectCodeActionsWithRuleApplication(RuleApplication ruleApp, Predicate predicate,
+ List codeActionInfos)
+ {
+ if (predicate.Invoke(ruleApp))
+ codeActionInfos.AddRange(ruleApp.Rule.SupportedCodeActions.Select(a => new CodeActionInfo
+ {
+ RuleApplication = ruleApp,
+ Action = a.Action,
+ CommandIdentifier = a.CommandIdentifier,
+ Arguments = a.Arguments,
+ Diagnostics = a.Diagnostics,
+ Kind = a.Kind,
+ CommandTitle = a.CommandTitle,
+ Title = a.Title,
+ DiagnosticIdentifier = a.DiagnosticIdentifier,
+ WorkspaceEdit = a.WorkspaceEdit,
+ IsPreferred = a.IsPreferred
+ }));
+ }
+ }
+}
\ No newline at end of file
diff --git a/AnyText/AnyText.Core/Rules/MultiRuleApplication.cs b/AnyText/AnyText.Core/Rules/MultiRuleApplication.cs
index e5ce2894..d68b7b13 100644
--- a/AnyText/AnyText.Core/Rules/MultiRuleApplication.cs
+++ b/AnyText/AnyText.Core/Rules/MultiRuleApplication.cs
@@ -60,6 +60,16 @@ public override void Deactivate(ParseContext context)
}
base.Deactivate(context);
}
+
+ ///
+ public override void AddCodeLenses(ICollection codeLenses, Predicate predicate = null)
+ {
+ foreach (var ruleApplication in Inner)
+ {
+ ruleApplication.AddCodeLenses(codeLenses, predicate);
+ }
+ base.AddCodeLenses(codeLenses, predicate);
+ }
internal override RuleApplication MigrateTo(MultiRuleApplication multiRule, ParseContext context)
{
diff --git a/AnyText/AnyText.Core/Rules/Rule.cs b/AnyText/AnyText.Core/Rules/Rule.cs
index 2d016875..b686aee3 100644
--- a/AnyText/AnyText.Core/Rules/Rule.cs
+++ b/AnyText/AnyText.Core/Rules/Rule.cs
@@ -221,5 +221,15 @@ public uint? TokenModifierIndex
get;
internal set;
}
+
+ ///
+ /// Gets the list of code actions for this rule.
+ ///
+ public virtual IEnumerable SupportedCodeActions => Enumerable.Empty();
+
+ ///
+ /// Gets the list of code lenses for this rule.
+ ///
+ public virtual IEnumerable SupportedCodeLenses => Enumerable.Empty();
}
}
diff --git a/AnyText/AnyText.Core/Rules/RuleApplication.cs b/AnyText/AnyText.Core/Rules/RuleApplication.cs
index 2cb0489e..8cd7ae74 100644
--- a/AnyText/AnyText.Core/Rules/RuleApplication.cs
+++ b/AnyText/AnyText.Core/Rules/RuleApplication.cs
@@ -212,7 +212,33 @@ public virtual void Deactivate(ParseContext context)
///
/// A collection of parse errors
public virtual IEnumerable CreateParseErrors() => Enumerable.Empty();
-
+
+ ///
+ /// Adds all CodeLens information of this to the provided collection.
+ ///
+ /// The collection to which the objects will be added.
+ /// An optional predicate that filters which rule applications should have their CodeLenses added. Default is true for all.
+ public virtual void AddCodeLenses(ICollection codeLenses, Predicate predicate = null)
+ {
+ predicate ??= _ => true;
+
+ if (Rule.SupportedCodeLenses.Any() && predicate.Invoke(this))
+ {
+ var ruleCodeLenses = Rule.SupportedCodeLenses.Select(a => new CodeLensInfo()
+ {
+ Arguments = a.Arguments,
+ CommandIdentifier = a.CommandIdentifier,
+ Data = a.Data,
+ Title = a.Title,
+ RuleApplication = this
+ });
+ foreach (var codeLens in ruleCodeLenses)
+ {
+ codeLenses.Add(codeLens);
+ }
+ }
+ }
+
///
/// Gets called when the newPosition of the given rule application changes
///
diff --git a/AnyText/AnyText.Core/Rules/SingleRuleApplication.cs b/AnyText/AnyText.Core/Rules/SingleRuleApplication.cs
index 14b71a2d..8c172893 100644
--- a/AnyText/AnyText.Core/Rules/SingleRuleApplication.cs
+++ b/AnyText/AnyText.Core/Rules/SingleRuleApplication.cs
@@ -47,6 +47,15 @@ public override void Deactivate(ParseContext context)
}
base.Deactivate(context);
}
+ ///
+ public override void AddCodeLenses(ICollection codeLenses, Predicate predicate = null)
+ {
+ if (Inner != null)
+ {
+ Inner.AddCodeLenses(codeLenses, predicate);
+ }
+ base.AddCodeLenses(codeLenses, predicate);
+ }
internal override RuleApplication MigrateTo(SingleRuleApplication singleRule, ParseContext context)
{
diff --git a/AnyText/AnyText.Core/Workspace/ChangeAnnotation.cs b/AnyText/AnyText.Core/Workspace/ChangeAnnotation.cs
new file mode 100644
index 00000000..fdea0fbc
--- /dev/null
+++ b/AnyText/AnyText.Core/Workspace/ChangeAnnotation.cs
@@ -0,0 +1,23 @@
+namespace NMF.AnyText.Workspace
+{
+ ///
+ /// Represents metadata or instructions for an annotation associated with a change.
+ ///
+ public class ChangeAnnotation
+ {
+ ///
+ /// A label for the annotation (e.g., "Refactor").
+ ///
+ public string Label { get; set; }
+
+ ///
+ /// Indicates if the change requires user confirmation.
+ ///
+ public bool? NeedsConfirmation { get; set; }
+
+ ///
+ /// A description or explanation of the annotation.
+ ///
+ public string Description { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/AnyText/AnyText.Core/Workspace/CreateFile.cs b/AnyText/AnyText.Core/Workspace/CreateFile.cs
new file mode 100644
index 00000000..d2b1ee38
--- /dev/null
+++ b/AnyText/AnyText.Core/Workspace/CreateFile.cs
@@ -0,0 +1,28 @@
+namespace NMF.AnyText.Workspace
+{
+ ///
+ /// Represents the information needed to create a new file.
+ ///
+ public class CreateFile
+ {
+ ///
+ /// The type of file creation (e.g., "create").
+ ///
+ public string Kind { get; set; }
+
+ ///
+ /// The URI of the file to be created.
+ ///
+ public string Uri { get; set; }
+
+ ///
+ /// File options (e.g., whether to overwrite an existing file).
+ ///
+ public FileOptions Options { get; set; }
+
+ ///
+ /// An optional annotation ID related to the file creation.
+ ///
+ public string AnnotationId { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/AnyText/AnyText.Core/Workspace/DeleteFile.cs b/AnyText/AnyText.Core/Workspace/DeleteFile.cs
new file mode 100644
index 00000000..79bcff21
--- /dev/null
+++ b/AnyText/AnyText.Core/Workspace/DeleteFile.cs
@@ -0,0 +1,28 @@
+namespace NMF.AnyText.Workspace
+{
+ ///
+ /// Represents the information needed to delete an existing file.
+ ///
+ public class DeleteFile
+ {
+ ///
+ /// The type of file operation (e.g., "delete").
+ ///
+ public string Kind { get; set; }
+
+ ///
+ /// The URI of the file to be deleted.
+ ///
+ public string Uri { get; set; }
+
+ ///
+ /// File deletion options (e.g., whether to delete recursively).
+ ///
+ public DeleteFileOptions Options { get; set; }
+
+ ///
+ /// An optional annotation ID related to the file deletion.
+ ///
+ public string AnnotationId { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/AnyText/AnyText.Core/Workspace/DeleteFileOptions.cs b/AnyText/AnyText.Core/Workspace/DeleteFileOptions.cs
new file mode 100644
index 00000000..37546c97
--- /dev/null
+++ b/AnyText/AnyText.Core/Workspace/DeleteFileOptions.cs
@@ -0,0 +1,18 @@
+namespace NMF.AnyText.Workspace
+{
+ ///
+ /// Options for deleting files, such as recursive deletion and handling missing files.
+ ///
+ public class DeleteFileOptions
+ {
+ ///
+ /// If true, delete directories recursively.
+ ///
+ public bool? Recursive { get; set; }
+
+ ///
+ /// If true, ignore the operation if the file does not exist.
+ ///
+ public bool? IgnoreIfNotExists { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/AnyText/AnyText.Core/Workspace/DocumentChange.cs b/AnyText/AnyText.Core/Workspace/DocumentChange.cs
new file mode 100644
index 00000000..49c134c8
--- /dev/null
+++ b/AnyText/AnyText.Core/Workspace/DocumentChange.cs
@@ -0,0 +1,28 @@
+namespace NMF.AnyText.Workspace
+{
+ ///
+ /// Represents a change to a document, including text edits, file creation, renaming, or deletion.
+ ///
+ public class DocumentChange
+ {
+ ///
+ /// Text document edits (e.g., line insertions, deletions).
+ ///
+ public TextDocumentEdit TextDocumentEdit { get; set; }
+
+ ///
+ /// Information for creating a new file.
+ ///
+ public CreateFile CreateFile { get; set; }
+
+ ///
+ /// Information for renaming an existing file.
+ ///
+ public RenameFile RenameFile { get; set; }
+
+ ///
+ /// Information for deleting an existing file.
+ ///
+ public DeleteFile DeleteFile { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/AnyText/AnyText.Core/Workspace/FileOptions.cs b/AnyText/AnyText.Core/Workspace/FileOptions.cs
new file mode 100644
index 00000000..c5b55509
--- /dev/null
+++ b/AnyText/AnyText.Core/Workspace/FileOptions.cs
@@ -0,0 +1,18 @@
+namespace NMF.AnyText.Workspace
+{
+ ///
+ /// Options for creating or renaming files, such as overwrite behavior.
+ ///
+ public class FileOptions
+ {
+ ///
+ /// If true, overwrite an existing file.
+ ///
+ public bool? Overwrite { get; set; }
+
+ ///
+ /// If true, ignore the operation if the file already exists.
+ ///
+ public bool? IgnoreIfExists { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/AnyText/AnyText.Core/Workspace/OptionalVersionedTextDocumentIdentifier.cs b/AnyText/AnyText.Core/Workspace/OptionalVersionedTextDocumentIdentifier.cs
new file mode 100644
index 00000000..1569094a
--- /dev/null
+++ b/AnyText/AnyText.Core/Workspace/OptionalVersionedTextDocumentIdentifier.cs
@@ -0,0 +1,18 @@
+namespace NMF.AnyText.Workspace
+{
+ ///
+ /// Identifies a text document with optional versioning.
+ ///
+ public class OptionalVersionedTextDocumentIdentifier
+ {
+ ///
+ /// The URI of the text document.
+ ///
+ public string Uri { get; set; }
+
+ ///
+ /// An optional version number for the document, if versioning is supported.
+ ///
+ public int? Version { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/AnyText/AnyText.Core/Workspace/RenameFile.cs b/AnyText/AnyText.Core/Workspace/RenameFile.cs
new file mode 100644
index 00000000..e8ce8fd6
--- /dev/null
+++ b/AnyText/AnyText.Core/Workspace/RenameFile.cs
@@ -0,0 +1,33 @@
+namespace NMF.AnyText.Workspace
+{
+ ///
+ /// Represents the information needed to rename an existing file.
+ ///
+ public class RenameFile
+ {
+ ///
+ /// The type of file operation (e.g., "rename").
+ ///
+ public string Kind { get; set; }
+
+ ///
+ /// The URI of the old file name.
+ ///
+ public string OldUri { get; set; }
+
+ ///
+ /// The URI of the new file name.
+ ///
+ public string NewUri { get; set; }
+
+ ///
+ /// File options (e.g., whether to overwrite).
+ ///
+ public FileOptions Options { get; set; }
+
+ ///
+ /// An optional annotation ID related to the file rename.
+ ///
+ public string AnnotationId { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/AnyText/AnyText.Core/Workspace/TextDocumentEdit.cs b/AnyText/AnyText.Core/Workspace/TextDocumentEdit.cs
new file mode 100644
index 00000000..9def3db5
--- /dev/null
+++ b/AnyText/AnyText.Core/Workspace/TextDocumentEdit.cs
@@ -0,0 +1,18 @@
+namespace NMF.AnyText.Workspace
+{
+ ///
+ /// Represents the text document edit instructions, including the document and the edits.
+ ///
+ public class TextDocumentEdit
+ {
+ ///
+ /// Identifies the text document to edit, including optional version information.
+ ///
+ public OptionalVersionedTextDocumentIdentifier TextDocument { get; set; }
+
+ ///
+ /// An Array of edits to perform on the document (e.g., insertions, deletions).
+ ///
+ public TextEdit[] Edits { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/AnyText/AnyText.Core/Workspace/WorkspaceEdit.cs b/AnyText/AnyText.Core/Workspace/WorkspaceEdit.cs
new file mode 100644
index 00000000..534b36d3
--- /dev/null
+++ b/AnyText/AnyText.Core/Workspace/WorkspaceEdit.cs
@@ -0,0 +1,26 @@
+using System.Collections.Generic;
+
+namespace NMF.AnyText.Workspace
+{
+ ///
+ /// Represents changes to a workspace, including text edits, document changes, and change annotations.
+ ///
+ public class WorkspaceEdit
+ {
+ ///
+ /// A dictionary of changes to text documents, keyed by document URI, with the value being the text edits for that
+ /// document.
+ ///
+ public Dictionary Changes { get; set; }
+
+ ///
+ /// A list of document-level changes (e.g., file creation, renaming, deletion).
+ ///
+ public List DocumentChanges { get; set; }
+
+ ///
+ /// A dictionary of annotations associated with changes, keyed by annotation ID.
+ ///
+ public Dictionary ChangeAnnotations { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/AnyText/AnyText.Lsp/ILspServer.CodeAction.cs b/AnyText/AnyText.Lsp/ILspServer.CodeAction.cs
new file mode 100644
index 00000000..6f297aa8
--- /dev/null
+++ b/AnyText/AnyText.Lsp/ILspServer.CodeAction.cs
@@ -0,0 +1,16 @@
+using LspTypes;
+using Newtonsoft.Json.Linq;
+using StreamJsonRpc;
+
+namespace NMF.AnyText
+{
+ public partial interface ILspServer
+ {
+ ///
+ /// Handles the textDocument/codeAction request from the client.
+ ///
+ /// The JSON token containing the parameters of the request. (CodeActionParams)
+ [JsonRpcMethod(Methods.TextDocumentCodeActionName)]
+ CodeAction[] CodeAction(JToken arg);
+ }
+}
\ No newline at end of file
diff --git a/AnyText/AnyText.Lsp/ILspServer.CodeLens.cs b/AnyText/AnyText.Lsp/ILspServer.CodeLens.cs
new file mode 100644
index 00000000..99dd1487
--- /dev/null
+++ b/AnyText/AnyText.Lsp/ILspServer.CodeLens.cs
@@ -0,0 +1,24 @@
+using LspTypes;
+using Newtonsoft.Json.Linq;
+using StreamJsonRpc;
+
+namespace NMF.AnyText
+{
+ public partial interface ILspServer
+ {
+ ///
+ /// Handles the textDocument/codeLens request from the client.
+ ///
+ /// The JSON token containing the parameters of the request. (CodeLensParams)
+ /// A Array of objects containing the available CodeLenses of the document.
+ [JsonRpcMethod(Methods.TextDocumentCodeLensName)]
+ CodeLens[] CodeLens(JToken arg);
+ ///
+ /// Handles the codeLense/resolve request from the client.
+ ///
+ /// The JSON token containing the parameters of the request. (CodeLens)
+ /// A object containing the executed CodeLens
+ [JsonRpcMethod(Methods.CodeLensResolveName)]
+ CodeLens CodeLensResolve(JToken arg);
+ }
+}
\ No newline at end of file
diff --git a/AnyText/AnyText.Lsp/ILspServer.cs b/AnyText/AnyText.Lsp/ILspServer.cs
index ab067e6f..19caab10 100644
--- a/AnyText/AnyText.Lsp/ILspServer.cs
+++ b/AnyText/AnyText.Lsp/ILspServer.cs
@@ -43,11 +43,19 @@ public InitializeResult Initialize(
void Shutdown();
///
- /// Handles the */setTrace request from the client. This is used to set the trace setting of the server.
+ /// Handles the $/setTrace request from the client. This is used to set the trace setting of the server.
///
/// The JSON token containing the parameters of the request. (SetTraceParams)
[JsonRpcMethod(MethodConstants.SetTrace)]
public void SetTrace(JToken arg);
+ ///
+ /// Handles the workspace/ececuteCommand request from the client. This is used to execute an action on the
+ /// Server.
+ ///
+ /// The JSON token containing the parameters of the request. (ExceuteCommandParams)
+ [JsonRpcMethod(Methods.WorkspaceExecuteCommandName)]
+ public void ExecuteCommand(JToken arg);
+
}
}
\ No newline at end of file
diff --git a/AnyText/AnyText.Lsp/LspServer.CodeAction.cs b/AnyText/AnyText.Lsp/LspServer.CodeAction.cs
new file mode 100644
index 00000000..a017103c
--- /dev/null
+++ b/AnyText/AnyText.Lsp/LspServer.CodeAction.cs
@@ -0,0 +1,294 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using LspTypes;
+using Newtonsoft.Json.Linq;
+using NMF.AnyText.Rules;
+using NMF.AnyText.Workspace;
+using Range = LspTypes.Range;
+using ChangeAnnotation = NMF.AnyText.Workspace.ChangeAnnotation;
+using CreateFile = NMF.AnyText.Workspace.CreateFile;
+using DeleteFile = NMF.AnyText.Workspace.DeleteFile;
+using DeleteFileOptions = LspTypes.DeleteFileOptions;
+using OptionalVersionedTextDocumentIdentifier = LspTypes.OptionalVersionedTextDocumentIdentifier;
+using RenameFile = NMF.AnyText.Workspace.RenameFile;
+using TextDocumentEdit = NMF.AnyText.Workspace.TextDocumentEdit;
+using WorkspaceEdit = NMF.AnyText.Workspace.WorkspaceEdit;
+
+namespace NMF.AnyText
+{
+ public partial class LspServer
+ {
+ private readonly Dictionary _codeActionRuleApplications = new();
+ ///
+ public CodeAction[] CodeAction(JToken arg)
+ {
+ var request = arg.ToObject();
+
+ var codeActions = new List();
+ if (!_documents.TryGetValue(request.TextDocument.Uri, out var document))
+ return codeActions.ToArray();
+
+ _codeActionRuleApplications.Clear();
+
+ var codeActionCapabilities = _clientCapabilities?.TextDocument?.CodeAction;
+ var supportsIsPreferred = codeActionCapabilities?.IsPreferredSupport == true;
+
+ var documentUri = request.TextDocument.Uri;
+ var diagnostics = request.Context.Diagnostics;
+ var kindFilter = request.Context.Only;
+
+ var startPosition = AsParsePosition(request.Range.Start);
+ var endPosition = AsParsePosition(request.Range.End);
+
+ var actions = document.GetCodeActionInfo(startPosition, endPosition);
+
+
+
+ foreach (var action in actions)
+ {
+ var guid = Guid.NewGuid().ToString();
+ _codeActionRuleApplications.TryAdd(guid, action.RuleApplication);
+
+ var diagnosticIdentifier = action.DiagnosticIdentifier;
+ var relevantDiagnostics = string.IsNullOrEmpty(diagnosticIdentifier) ? new Diagnostic[] {} :diagnostics
+ .Where(d => d.Message.Contains(diagnosticIdentifier))
+ .ToArray();
+
+ if (!string.IsNullOrEmpty(diagnosticIdentifier) && relevantDiagnostics.Length == 0)
+ continue;
+
+ var actionKind = !string.IsNullOrEmpty(action.Kind) ? ParseLspCodeActionKind(action.Kind) : null;
+ if (kindFilter != null && kindFilter.Any() && actionKind != null &&
+ !kindFilter.Contains(actionKind.Value)) continue;
+
+ var workspaceEdit = action.WorkspaceEdit?.Invoke(new ExecuteCommandArguments()
+ {
+ RuleApplication = action.RuleApplication,
+ Context = document.Context,
+ DocumentUri = documentUri,
+ OtherOptions = action.Arguments
+ });
+
+ var arguments = new object[] { documentUri, guid };
+ codeActions.Add(new CodeAction
+ {
+ Title = action.Title,
+ Kind = actionKind,
+ Diagnostics = relevantDiagnostics.Length == 0 ? null : relevantDiagnostics,
+ Edit = action.WorkspaceEdit != null ? MapWorkspaceEdit(workspaceEdit) : null,
+ IsPreferred = supportsIsPreferred && action.IsPreferred ? true : null,
+ Command = action.CommandIdentifier != null
+ ? new Command
+ {
+ Title = action.CommandTitle,
+ CommandIdentifier = action.CommandIdentifier,
+ Arguments = action.Arguments != null
+ ? arguments.Concat(action.Arguments.Cast