diff --git a/AnyText/AnyText.Lsp/AnyTextJsonRpcServerUtil.cs b/AnyText/AnyText.Lsp/AnyTextJsonRpcServerUtil.cs index c4ad0986..634b3313 100644 --- a/AnyText/AnyText.Lsp/AnyTextJsonRpcServerUtil.cs +++ b/AnyText/AnyText.Lsp/AnyTextJsonRpcServerUtil.cs @@ -11,8 +11,6 @@ namespace NMF.AnyText /// public static class AnyTextJsonRpcServerUtil { - private static readonly TraceSource _traceSource = CreateTraceSource(); - /// /// Creates a StreamJSON RPC object for the given transport /// @@ -22,7 +20,6 @@ public static class AnyTextJsonRpcServerUtil public static JsonRpc CreateServer(WebSocket webSocket, ILspServer server) { var rpc = new JsonRpc(new WebSocketMessageHandler(webSocket, CreateFormatter())); - rpc.TraceSource = _traceSource; rpc.AddLocalRpcTarget(server, CreateTargetOptions()); return rpc; } @@ -36,7 +33,6 @@ public static JsonRpc CreateServer(WebSocket webSocket, ILspServer server) public static JsonRpc CreateServer(IDuplexPipe pipe, ILspServer server) { var rpc = new JsonRpc(new HeaderDelimitedMessageHandler(pipe, CreateFormatter())); - rpc.TraceSource = _traceSource; rpc.AddLocalRpcTarget(server, CreateTargetOptions()); return rpc; } @@ -50,7 +46,6 @@ public static JsonRpc CreateServer(IDuplexPipe pipe, ILspServer server) public static JsonRpc CreateServer(Stream stream, ILspServer server) { var rpc = new JsonRpc(new HeaderDelimitedMessageHandler(stream, CreateFormatter())); - rpc.TraceSource = _traceSource; rpc.AddLocalRpcTarget(server, CreateTargetOptions()); return rpc; } @@ -62,7 +57,6 @@ public static JsonRpc CreateServer(Stream stream, ILspServer server) public static JsonRpc CreateServer(Stream stream) { var rpc = new JsonRpc(new HeaderDelimitedMessageHandler(stream, CreateFormatter())); - rpc.TraceSource = _traceSource; return rpc; } @@ -84,15 +78,20 @@ private static JsonRpcTargetOptions CreateTargetOptions() EventNameTransform = name => name.ToLowerInvariant() }; } - - private static TraceSource CreateTraceSource() + /// + /// Creates and configures a instance for logging trace information. + /// + /// The SourceLevel used to filter messages by type and severity. Defaults to . + /// A instance configured for the specified logging level. + public static TraceSource CreateTraceSource(SourceLevels sourceLevels = SourceLevels.All) { - var traceSource = new TraceSource("LSP", SourceLevels.All); - // use error stream such that VS Code can see the stdout - traceSource.Listeners.Add(new ConsoleTraceListener(true)); + var traceSource = new TraceSource("LSP", sourceLevels); + // Use error stream (stderr) so that VS Code can capture the output + traceSource.Listeners.Add(new ConsoleTraceListener(useErrorStream: true)); return traceSource; } + private static IJsonRpcMessageFormatter CreateFormatter() { return new JsonMessageFormatter(); diff --git a/AnyText/AnyText.Lsp/ILspServer.cs b/AnyText/AnyText.Lsp/ILspServer.cs index 0a0cb29c..5919e241 100644 --- a/AnyText/AnyText.Lsp/ILspServer.cs +++ b/AnyText/AnyText.Lsp/ILspServer.cs @@ -41,5 +41,13 @@ public InitializeResult Initialize( [JsonRpcMethod(Methods.ShutdownName)] void Shutdown(); + + /// + /// 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); + } } \ No newline at end of file diff --git a/AnyText/AnyText.Lsp/LspServer.Diagnostics.cs b/AnyText/AnyText.Lsp/LspServer.Diagnostics.cs index 1afcce79..e05e2878 100644 --- a/AnyText/AnyText.Lsp/LspServer.Diagnostics.cs +++ b/AnyText/AnyText.Lsp/LspServer.Diagnostics.cs @@ -10,6 +10,7 @@ public partial class LspServer private async void SendDiagnostics(string uri, ParseContext context) { + SendLogMessage(MessageType.Info, $"Starting diagnostics generation for URI: {uri}"); var diagnostics = new List(); var errors = context.Errors; foreach (var error in errors) @@ -38,10 +39,13 @@ private async void SendDiagnostics(string uri, ParseContext context) try { await _rpc.NotifyWithParameterObjectAsync(Methods.TextDocumentPublishDiagnosticsName, diagnosticsParams); + SendLogMessage(MessageType.Info, $"Diagnostics published successfully for URI: {uri} with {diagnostics.Count} issue(s)."); } catch (Exception ex) { - Console.Error.WriteLine($"Error publishing Diagnostics: {ex.Message}"); + var errorMessage = $"Error publishing diagnostics for URI: {uri}. Exception: {ex.Message}"; + SendLogMessage(MessageType.Error, errorMessage); + Console.Error.WriteLine(errorMessage); } } } diff --git a/AnyText/AnyText.Lsp/LspServer.Registration.cs b/AnyText/AnyText.Lsp/LspServer.Registration.cs index ddaed1b7..a005b221 100644 --- a/AnyText/AnyText.Lsp/LspServer.Registration.cs +++ b/AnyText/AnyText.Lsp/LspServer.Registration.cs @@ -35,11 +35,15 @@ private async void RegisterCapabilities(Registration[] registrations) try { + SendLogMessage(MessageType.Info, "Sending capabilities registration request to client."); await _rpc.InvokeWithParameterObjectAsync(Methods.ClientRegisterCapabilityName, registrationParams); + SendLogMessage(MessageType.Info, "Capabilities registration request completed successfully."); } catch (Exception ex) { - Console.Error.WriteLine($"Error register capabilities: {ex.Message}"); + var errorMessage = $"Error registering capabilities: {ex.Message}"; + SendLogMessage(MessageType.Error, errorMessage); + Console.Error.WriteLine(errorMessage); } } diff --git a/AnyText/AnyText.Lsp/LspServer.SemanticToken.cs b/AnyText/AnyText.Lsp/LspServer.SemanticToken.cs index d103a14e..4697dd9f 100644 --- a/AnyText/AnyText.Lsp/LspServer.SemanticToken.cs +++ b/AnyText/AnyText.Lsp/LspServer.SemanticToken.cs @@ -13,6 +13,7 @@ public partial class LspServer /// public SemanticTokens QuerySemanticTokens(JToken arg) { + SendLogMessage(MessageType.Info, "Received request for full semantic tokens."); var semanticTokensParams = arg.ToObject(); var uri = semanticTokensParams.TextDocument.Uri; @@ -33,6 +34,7 @@ public SemanticTokens QuerySemanticTokens(JToken arg) /// public SemanticTokensDelta QuerySemanticTokensDelta(JToken arg) { + SendLogMessage(MessageType.Info, "Received request for semantic tokens delta."); var semanticTokensParams = arg.ToObject(); var uri = semanticTokensParams.TextDocument.Uri; @@ -74,6 +76,7 @@ public SemanticTokensDelta QuerySemanticTokensDelta(JToken arg) /// public SemanticTokens QuerySemanticTokensRange(JToken arg) { + SendLogMessage(MessageType.Info, "Received request for semantic tokens in a range."); var semanticTokensRangeParams = arg.ToObject(); var uri = semanticTokensRangeParams.TextDocument.Uri; var range = semanticTokensRangeParams.Range; diff --git a/AnyText/AnyText.Lsp/LspServer.Trace.cs b/AnyText/AnyText.Lsp/LspServer.Trace.cs new file mode 100644 index 00000000..61a33a2d --- /dev/null +++ b/AnyText/AnyText.Lsp/LspServer.Trace.cs @@ -0,0 +1,34 @@ +using LspTypes; +using Newtonsoft.Json.Linq; +using System.Diagnostics; + +namespace NMF.AnyText +{ + public partial class LspServer + { + /// + public void SetTrace(JToken arg) + { + var setTraceParams = arg.ToObject(); + + UpdateTraceSource(setTraceParams.Value); + SendLogMessage(MessageType.Info, $"Trace level updated to: {setTraceParams.Value}"); + } + + private void UpdateTraceSource(TraceValue traceValue) + { + _rpc.TraceSource = + AnyTextJsonRpcServerUtil.CreateTraceSource(MapTraceValueToSourceLevels(traceValue)); + } + private static SourceLevels MapTraceValueToSourceLevels(TraceValue traceValue) + { + return traceValue switch + { + TraceValue.Off => SourceLevels.Off, + TraceValue.Messages => SourceLevels.Information, + TraceValue.Verbose => SourceLevels.Verbose, + _ => SourceLevels.All + }; + } + } +} \ No newline at end of file diff --git a/AnyText/AnyText.Lsp/LspServer.cs b/AnyText/AnyText.Lsp/LspServer.cs index ae3b8af8..a2af3d9c 100644 --- a/AnyText/AnyText.Lsp/LspServer.cs +++ b/AnyText/AnyText.Lsp/LspServer.cs @@ -1,4 +1,4 @@ -using LspTypes; +using LspTypes; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using NMF.AnyText.Grammars; @@ -39,7 +39,6 @@ public LspServer(IEnumerable grammars) _languages = grammars?.ToDictionary(sp => sp.LanguageId); } - [JsonRpcMethod(Methods.InitializeName)] public InitializeResult Initialize( int? processId , _InitializeParams_ClientInfo clientInfo @@ -72,8 +71,12 @@ public InitializeResult Initialize( WorkDoneProgress = false } }; + UpdateTraceSource(trace); + + SendLogMessage(MessageType.Info, "LSP Server initialization completed."); return new InitializeResult { Capabilities = serverCapabilities }; } + public void Initialized() { } public void Shutdown() { } @@ -87,11 +90,13 @@ public void DidChange(JToken arg) { document.Update(changes.ContentChanges.Select(AsTextEdit)); SendDiagnostics(changes.TextDocument.Uri, document.Context); + SendLogMessage(MessageType.Info, $"Document {changes.TextDocument.Uri} updated."); } } public void DidSave(TextDocumentIdentifier textDocument, string text) { + SendLogMessage(MessageType.Info, $"Document {textDocument.Uri} saved."); } private static ParsePosition AsParsePosition(Position position) => new ParsePosition((int)position.Line, (int)position.Character); @@ -108,6 +113,7 @@ public void DidClose(JToken arg) if (_documents.TryGetValue(closeParams.TextDocument.Uri, out var document)) { _documents.Remove(closeParams.TextDocument.Uri); + SendLogMessage(MessageType.Info, $"Document {closeParams.TextDocument.Uri} closed."); } } @@ -126,20 +132,43 @@ public void DidOpen(JToken arg) RegisterCapabilitiesOnOpen(openParams.TextDocument.LanguageId, parser); SendDiagnostics(openParams.TextDocument.Uri, parser.Context); + SendLogMessage(MessageType.Info, $"Document {openParams.TextDocument.Uri} opened with language {openParams.TextDocument.LanguageId}."); } else { - throw new NotSupportedException($"No grammar found for extension {openParams.TextDocument.LanguageId}"); + var errorMessage = $"No grammar found for extension {openParams.TextDocument.LanguageId}"; + SendLogMessage(MessageType.Error, errorMessage); + throw new NotSupportedException(errorMessage); } } else { - throw new NotSupportedException($"Cannot open URI {openParams.TextDocument.Uri}"); + var errorMessage = $"Cannot open URI {openParams.TextDocument.Uri}"; + SendLogMessage(MessageType.Error, errorMessage); + throw new NotSupportedException(errorMessage); } } public void Exit() { + SendLogMessage(MessageType.Info, "LSP Server exiting."); throw new NotImplementedException(); } + + // + /// Sends a log message to the client. + /// + /// The type of the message (Info, Warning, Error). + /// The message content. + private void SendLogMessage(MessageType type, string message) + { + var logMessageParams = new LogMessageParams + { + MessageType = type, + Message = message + }; + + _rpc.NotifyWithParameterObjectAsync(Methods.WindowLogMessageName, logMessageParams); + + } } } \ No newline at end of file diff --git a/AnyText/AnyText.Lsp/MethodConstants.cs b/AnyText/AnyText.Lsp/MethodConstants.cs index 2327fe9d..3054d8d1 100644 --- a/AnyText/AnyText.Lsp/MethodConstants.cs +++ b/AnyText/AnyText.Lsp/MethodConstants.cs @@ -3,5 +3,6 @@ namespace NMF.AnyText public static class MethodConstants { public const string RegisterSemanticTokens = "textDocument/semanticTokens"; + public const string SetTrace = "$/setTrace"; } } \ No newline at end of file diff --git a/AnyText/Tests/AnyTextExtension/package.json b/AnyText/Tests/AnyTextExtension/package.json index 14dee364..55fb94a7 100644 --- a/AnyText/Tests/AnyTextExtension/package.json +++ b/AnyText/Tests/AnyTextExtension/package.json @@ -38,13 +38,13 @@ "type": "object", "title": "Example configuration", "properties": { - "languageServerExample.maxNumberOfProblems": { + "anytext.maxNumberOfProblems": { "scope": "resource", "type": "number", "default": 100, "description": "Controls the maximum number of problems produced by the server." }, - "languageServerExample.trace.server": { + "anytext.trace.server": { "scope": "window", "type": "string", "enum": [ diff --git a/Glsp/Glsp/Processing/Layouting/LayeredLayoutService.cs b/Glsp/Glsp/Processing/Layouting/LayeredLayoutService.cs index c96c1dde..8c37aa65 100644 --- a/Glsp/Glsp/Processing/Layouting/LayeredLayoutService.cs +++ b/Glsp/Glsp/Processing/Layouting/LayeredLayoutService.cs @@ -20,15 +20,35 @@ public class LayeredLayoutService : AglLayoutService /// public static readonly LayeredLayoutService Instance = new LayeredLayoutService(); + private static readonly SugiyamaLayoutSettings _defaultSettings = new SugiyamaLayoutSettings + { + Transformation = PlaneTransformation.Rotation(Math.PI / 2), + EdgeRoutingSettings = { EdgeRoutingMode = EdgeRoutingMode.Rectilinear } + }; + + /// + /// Creates a new instance + /// + public LayeredLayoutService() : this(null) { } + + /// + /// Creates a new instance + /// + /// layout settings + public LayeredLayoutService(SugiyamaLayoutSettings settings) + { + Settings = settings ?? _defaultSettings; + } + + /// + /// Gets the layout settings for this layout service + /// + public SugiyamaLayoutSettings Settings { get; } + /// protected override void ProcessLayout(GeometryGraph g) { - var settings = new SugiyamaLayoutSettings - { - Transformation = PlaneTransformation.Rotation(Math.PI / 2), - EdgeRoutingSettings = { EdgeRoutingMode = EdgeRoutingMode.Rectilinear } - }; - var layout = new LayeredLayout(g, settings); + var layout = new LayeredLayout(g, Settings); layout.Run(); } } diff --git a/Models/Models/Repository/MetaRepository.cs b/Models/Models/Repository/MetaRepository.cs index 2ca17999..fce4314d 100644 --- a/Models/Models/Repository/MetaRepository.cs +++ b/Models/Models/Repository/MetaRepository.cs @@ -13,11 +13,21 @@ namespace NMF.Models.Repository /// public sealed class MetaRepository : IModelRepository { - private static readonly MetaRepository instance = new MetaRepository(); + private static readonly MetaRepository instance; private readonly ModelCollection entries; private readonly ModelSerializer serializer = new ModelSerializer(); private readonly HashSet traversedAssemblies = new HashSet(); + /// + /// Initializes the type + /// + static MetaRepository() + { + // we need an explicit static type constructor because the runtime is sometimes too lazy + // to initialize static variables in time, but they are needed for static lookup operations + instance = new MetaRepository(); + } + event EventHandler IModelRepository.BubbledChange { add { }