Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion AnyText/AnyText.Core/CodeLensApplication.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
using NMF.AnyText.Rules;
using System.Collections;
using System.Collections.Generic;

namespace NMF.AnyText
{
Expand All @@ -7,7 +9,8 @@ namespace NMF.AnyText
/// </summary>
/// <param name="CodeLens">The code lens</param>
/// <param name="RuleApplication">The rule application instance</param>
public record struct CodeLensApplication(CodeLensInfo CodeLens, RuleApplication RuleApplication)
/// <param name="Arguments">Additional arguments</param>
public record struct CodeLensApplication(CodeLensInfo CodeLens, RuleApplication RuleApplication, IEnumerable<string> Arguments)
{
}
}
5 changes: 0 additions & 5 deletions AnyText/AnyText.Core/CodeLensInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,6 @@ public abstract class CodeLensInfo : ActionInfo
/// </summary>
public string CommandIdentifier { get; init; }

/// <summary>
/// Gets or sets the dictionary of arguments to be passed along with the command when invoked.
/// </summary>
public Dictionary<string, object> Arguments { get; init; }

/// <summary>
/// Gets or sets additional data associated with this CodeLens, which can be used for custom functionality.
/// </summary>
Expand Down
5 changes: 5 additions & 0 deletions AnyText/AnyText.Core/Matcher.cs
Original file line number Diff line number Diff line change
Expand Up @@ -641,6 +641,11 @@ public bool IsObsoleted(RuleApplication ruleApplication)
return ruleApplication.IterateLiterals(IsObsoleted);
}

/// <summary>
/// Deteremines whether the given rule application has an up to date position
/// </summary>
/// <param name="ruleApplication">the rule application to check</param>
/// <returns>true, if the position is serious, otherwise false</returns>
public bool IsFaithfulPosition(RuleApplication ruleApplication)
{
var column = ruleApplication.Column;
Expand Down
12 changes: 11 additions & 1 deletion AnyText/AnyText.Core/Parser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,16 @@ public object Initialize(string[] input, bool skipValidation = false)
/// <param name="skipValidation">if set to true, the parser does not perform validation rules (default: false)</param>
/// <exception cref="ArgumentException">thrown if no parse tree could be generated for the semantic object</exception>
public void Initialize(object semanticObject, bool skipValidation = false)
=> Initialize(semanticObject, skipValidation, null);

/// <summary>
/// Initializes the parser system with a semantic object
/// </summary>
/// <param name="semanticObject">the semantic object</param>
/// <param name="skipValidation">if set to true, the parser does not perform validation rules (default: false)</param>
/// <param name="indentString">the string used for indentation</param>
/// <exception cref="ArgumentException">thrown if no parse tree could be generated for the semantic object</exception>
public void Initialize(object semanticObject, bool skipValidation, string indentString)
{
var ruleApplication = _context.Grammar.Root.Synthesize(semanticObject, default, _context);
if (!ruleApplication.IsPositive)
Expand All @@ -122,7 +132,7 @@ public void Initialize(object semanticObject, bool skipValidation = false)
_context.ClearErrors();

var writer = new StringWriter();
var prettyWriter = new PrettyPrintWriter(writer, " ");
var prettyWriter = new PrettyPrintWriter(writer, indentString ?? " ");
ruleApplication.Write(prettyWriter, _context);

_context.Input = writer.ToString().Split(Environment.NewLine);
Expand Down
2 changes: 1 addition & 1 deletion AnyText/AnyText.Core/Rules/RuleApplication.cs
Original file line number Diff line number Diff line change
Expand Up @@ -448,7 +448,7 @@ internal virtual void AddCodeLenses(ICollection<CodeLensApplication> codeLenses,

if (Rule.SupportedCodeLenses.Any() && predicate.Invoke(this))
{
var ruleCodeLenses = Rule.SupportedCodeLenses.Select(a => new CodeLensApplication(a, this));
var ruleCodeLenses = Rule.SupportedCodeLenses.Select(a => new CodeLensApplication(a, this, null));
foreach (var codeLens in ruleCodeLenses)
{
codeLenses.Add(codeLens);
Expand Down
129 changes: 74 additions & 55 deletions AnyText/AnyText.Lsp/LspServer.CodeAction.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ namespace NMF.AnyText
{
public partial class LspServer
{
private readonly Dictionary<string, RuleApplication> _codeActionRuleApplications = new();
private readonly Dictionary<string, (Uri uri, RuleApplication ruleApplication)> _codeActionRuleApplications = new();
/// <inheritdoc cref="ILspServer.CodeAction" />
public CodeAction[] CodeAction(JToken arg)
{
Expand All @@ -19,68 +19,87 @@ public CodeAction[] CodeAction(JToken arg)
return Array.Empty<CodeAction>();

var codeActions = new List<CodeAction>();
_codeActionRuleApplications.Clear();

var codeActionCapabilities = _clientCapabilities?.TextDocument?.CodeAction;
var supportsIsPreferred = codeActionCapabilities?.IsPreferredSupport ?? false;

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 = GetCodeActionInfo(document, startPosition, endPosition);

foreach (var actionApplication in actions)
var uri = document.Context.FileUri;
lock (_codeActionRuleApplications)
{
var action = actionApplication.Action;
var guid = Guid.NewGuid().ToString();
_codeActionRuleApplications.TryAdd(guid, actionApplication.RuleApplication);
var assigned = _codeActionRuleApplications.Where(kv => kv.Value.uri == uri).ToDictionary(kv => kv.Value.ruleApplication, kv => kv.Key);
var used = new HashSet<string>();

var codeActionCapabilities = _clientCapabilities?.TextDocument?.CodeAction;
var supportsIsPreferred = codeActionCapabilities?.IsPreferredSupport ?? false;

var diagnosticIdentifier = action.DiagnosticIdentifier;
var relevantDiagnostics = string.IsNullOrEmpty(diagnosticIdentifier) ? Array.Empty<Diagnostic>() : diagnostics
.Where(d => d.Message.Contains(diagnosticIdentifier))
.ToArray();
var documentUri = request.TextDocument.Uri;
var diagnostics = request.Context.Diagnostics;
var kindFilter = request.Context.Only;

if (!string.IsNullOrEmpty(diagnosticIdentifier) && relevantDiagnostics.Length == 0)
continue;
var startPosition = AsParsePosition(request.Range.Start);
var endPosition = AsParsePosition(request.Range.End);

var actionKind = !string.IsNullOrEmpty(action.Kind) ? ParseLspCodeActionKind(action.Kind) : null;
if (kindFilter != null && kindFilter.Any() && actionKind != null &&
!kindFilter.Contains(actionKind.Value)) continue;
var actions = GetCodeActionInfo(document, startPosition, endPosition);

var workspaceEdit = action.CreateWorkspaceEdit(new LspCommandArguments(this)
foreach (var actionApplication in actions)
{
RuleApplication = actionApplication.RuleApplication,
Context = document.Context,
DocumentUri = documentUri,
OtherOptions = action.Arguments
});
var workspaceFolder = _workspaceFolders.FirstOrDefault()?.Uri;

var arguments = new object[] { documentUri, guid };
codeActions.Add(new CodeAction
var action = actionApplication.Action;
if (!assigned.TryGetValue(actionApplication.RuleApplication, out var guid))
{
guid = Guid.NewGuid().ToString();
_codeActionRuleApplications.Add(guid, (uri, actionApplication.RuleApplication));
assigned.Add(actionApplication.RuleApplication, guid);
}
else
{
used.Add(guid);
}

var diagnosticIdentifier = action.DiagnosticIdentifier;
var relevantDiagnostics = string.IsNullOrEmpty(diagnosticIdentifier) ? Array.Empty<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.CreateWorkspaceEdit(new LspCommandArguments(this)
{
RuleApplication = actionApplication.RuleApplication,
Context = document.Context,
DocumentUri = documentUri,
OtherOptions = action.Arguments
});
var workspaceFolder = _workspaceFolders.FirstOrDefault()?.Uri;

var arguments = new object[] { documentUri, guid };
codeActions.Add(new CodeAction
{
Title = action.Title,
Kind = actionKind,
Diagnostics = relevantDiagnostics.Length == 0 ? null : relevantDiagnostics,
Edit = workspaceEdit != null ? LspTypesMapper.MapWorkspaceEdit(workspaceEdit, workspaceFolder) : 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<object>()).ToArray()
: arguments.ToArray()
}
: null
});
}
foreach (var item in assigned.Values)
{
Title = action.Title,
Kind = actionKind,
Diagnostics = relevantDiagnostics.Length == 0 ? null : relevantDiagnostics,
Edit = workspaceEdit != null ? LspTypesMapper.MapWorkspaceEdit(workspaceEdit, workspaceFolder) : 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<object>()).ToArray()
: arguments.ToArray()
}
: null
});
if (!used.Contains(item))
{
_codeActionRuleApplications.Remove(item);
}
}
}

return codeActions.ToArray();
}

Expand Down
79 changes: 55 additions & 24 deletions AnyText/AnyText.Lsp/LspServer.CodeLens.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ namespace NMF.AnyText
{
public partial class LspServer
{
private readonly Dictionary<string, RuleApplication> _codeLensRuleApplications = new();
private readonly Dictionary<string, (Uri uri, RuleApplication ruleApplication)> _codeLensRuleApplications = new();

/// <inheritdoc cref="ILspServer.CodeLens" />
public CodeLens[] CodeLens(JToken arg)
Expand All @@ -25,34 +25,54 @@ public CodeLens[] CodeLens(JToken arg)
if (!_documents.TryGetValue(request.TextDocument.Uri, out var document))
return Array.Empty<CodeLens>();

_codeLensRuleApplications.Clear();

var codeLenses = GetCodeLenses(document).Select(r =>
var uri = document.Context.FileUri;
var codeLenses = new List<CodeLens>();
lock (_codeLensRuleApplications)
{
var codeLens = r.CodeLens;
var guid = Guid.NewGuid().ToString();
_codeLensRuleApplications.TryAdd(guid, r.RuleApplication);
var arguments = new object[] { request.TextDocument.Uri, guid };
var end = r.RuleApplication.CurrentPosition + r.RuleApplication.Length;
return new CodeLens
var assigned = _codeLensRuleApplications.Where(kv => kv.Value.uri == uri).ToDictionary(kv => kv.Value.ruleApplication, kv => kv.Key);
var used = new HashSet<string>();
foreach (var r in GetCodeLenses(document))
{
Command = new Command()
var codeLens = r.CodeLens;
if (!assigned.TryGetValue(r.RuleApplication, out var guid))
{
guid = Guid.NewGuid().ToString();
_codeLensRuleApplications.Add(guid, (uri, r.RuleApplication));
assigned.Add(r.RuleApplication, guid);
}
else
{
used.Add(guid);
}
var arguments = new object[] { request.TextDocument.Uri, guid };
var end = r.RuleApplication.CurrentPosition + r.RuleApplication.Length;
codeLenses.Add(new CodeLens
{
Title = codeLens.GetTitleForRuleApplication(r.RuleApplication, document.Context),
CommandIdentifier = codeLens.CommandIdentifier,
Arguments = codeLens.Arguments != null
? arguments.Concat(codeLens.Arguments.Cast<object>()).ToArray()
: arguments.ToArray()
},
Data = codeLens.Data,
Range = new Range()
Command = new Command()
{
Title = codeLens.GetTitleForRuleApplication(r.RuleApplication, document.Context),
CommandIdentifier = codeLens.CommandIdentifier,
Arguments = r.Arguments != null
? arguments.Concat(r.Arguments.Cast<object>()).ToArray()
: arguments.ToArray()
},
Data = codeLens.Data,
Range = new Range()
{
Start = new Position((uint)r.RuleApplication.CurrentPosition.Line, (uint)r.RuleApplication.CurrentPosition.Col),
End = new Position((uint)end.Line, (uint)end.Col)
}
});
}
foreach (var item in assigned.Values)
{
if (!used.Contains(item))
{
Start = new Position((uint)r.RuleApplication.CurrentPosition.Line, (uint)r.RuleApplication.CurrentPosition.Col),
End = new Position((uint)end.Line, (uint)end.Col)
_codeLensRuleApplications.Remove(item);
}
};
});
return codeLenses.ToArray();
}
}
return CodeLensesForDocument(document, codeLenses).ToArray();
}

private ICollection<CodeLensApplication> GetCodeLenses(Parser document)
Expand All @@ -68,6 +88,17 @@ private ICollection<CodeLensApplication> GetCodeLenses(Parser document)
}
}

/// <summary>
/// Calculates the code lenses for the given document
/// </summary>
/// <param name="codeLenses">the code lenses obtained from the document</param>
/// <param name="document">the document for which to calculate code lenses</param>
/// <returns>A collection of code lenses</returns>
protected virtual IEnumerable<CodeLens> CodeLensesForDocument(Parser document, IEnumerable<CodeLens> codeLenses)
{
return codeLenses;
}

/// <inheritdoc cref="ILspServer.CodeLensResolve" />
public CodeLens CodeLensResolve(JToken arg)
{
Expand Down
43 changes: 41 additions & 2 deletions AnyText/AnyText.Lsp/LspServer.ExecuteCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -96,8 +96,24 @@ private void ExecuteCommand(string commandIdentifier, object[] args)
private bool FindRuleApplication(object[] args, out RuleApplication actionRuleApplication)
{
var uid = args[1].ToString();
return _codeActionRuleApplications.TryGetValue(uid, out actionRuleApplication)
|| _codeLensRuleApplications.TryGetValue(uid, out actionRuleApplication);
lock (_codeActionRuleApplications)
{
if (_codeActionRuleApplications.TryGetValue(uid, out var actionRuleApplicationPair))
{
actionRuleApplication = actionRuleApplicationPair.ruleApplication;
return true;
}
}
lock (_codeLensRuleApplications)
{
if (_codeLensRuleApplications.TryGetValue(uid, out var actionRuleApplicationPair))
{
actionRuleApplication = actionRuleApplicationPair.ruleApplication;
return true;
}
}
actionRuleApplication = null;
return false;
}

private Registration CreateExecuteCommandRegistration(Grammar language)
Expand All @@ -113,5 +129,28 @@ private Registration CreateExecuteCommandRegistration(Grammar language)
Method = Methods.WorkspaceExecuteCommandName
};
}

private Registration CreateSystemCommandRegistration()
{
var systemCommands = SystemCommands;
if (systemCommands != null)
{
return new Registration
{
RegisterOptions = new ExecuteCommandRegistrationOptions
{
Commands = systemCommands.ToArray()
},
Id = Guid.NewGuid().ToString(),
Method = Methods.WorkspaceExecuteCommandName
};
}
return null;
}

/// <summary>
/// Gets a collection of available system commands
/// </summary>
protected virtual IEnumerable<string> SystemCommands => null;
}
}
Loading
Loading