-
-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Basic language server infrastructure.
Part of #62.
- Loading branch information
Showing
11 changed files
with
436 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,88 @@ | ||
using Vezel.Celerity.Driver.IO; | ||
|
||
namespace Vezel.Celerity.Driver.Logging; | ||
|
||
internal sealed class TerminalLanguageServiceLogger : LanguageServiceLogger | ||
{ | ||
private static readonly Color _timestampColor = Color.FromArgb(127, 127, 127); | ||
|
||
private static readonly Color _nameColor = Color.FromArgb(233, 233, 233); | ||
|
||
private static readonly Color _eventColor = Color.FromArgb(0, 155, 155); | ||
|
||
private static readonly Color _traceColor = Color.FromArgb(127, 0, 127); | ||
|
||
private static readonly Color _debugColor = Color.FromArgb(0, 127, 255); | ||
|
||
private static readonly Color _informationColor = Color.FromArgb(255, 255, 255); | ||
|
||
private static readonly Color _warningColor = Color.FromArgb(255, 255, 0); | ||
|
||
private static readonly Color _errorColor = Color.FromArgb(255, 63, 0); | ||
|
||
private static readonly Color _criticalColor = Color.FromArgb(255, 0, 0); | ||
|
||
private readonly TerminalLanguageServiceLoggerProvider _provider; | ||
|
||
private readonly string _name; | ||
|
||
public TerminalLanguageServiceLogger(TerminalLanguageServiceLoggerProvider provider, string name) | ||
{ | ||
_provider = provider; | ||
_name = name; | ||
} | ||
|
||
public override void Log(LogLevel logLevel, string eventName, string message, Exception? exception) | ||
{ | ||
var writer = _provider.Writer; | ||
|
||
writer.Write("["); | ||
writer.WriteControl( | ||
ControlSequences.SetForegroundColor(_timestampColor.R, _timestampColor.G, _timestampColor.B)); | ||
writer.Write($"{DateTime.Now:HH:mm:ss.fff}"); | ||
writer.WriteControl(ControlSequences.ResetAttributes()); | ||
writer.Write("]"); | ||
|
||
var (level, color) = logLevel switch | ||
{ | ||
LogLevel.Trace => ("TRC", _traceColor), | ||
LogLevel.Debug => ("DBG", _debugColor), | ||
LogLevel.Information => ("INF", _informationColor), | ||
LogLevel.Warning => ("WRN", _warningColor), | ||
LogLevel.Error => ("ERR", _errorColor), | ||
LogLevel.Critical => ("CRT", _criticalColor), | ||
_ => throw new UnreachableException(), | ||
}; | ||
|
||
writer.Write("["); | ||
writer.WriteControl(ControlSequences.SetForegroundColor(color.R, color.G, color.B)); | ||
writer.Write(level); | ||
writer.WriteControl(ControlSequences.ResetAttributes()); | ||
writer.Write("]"); | ||
|
||
writer.Write("["); | ||
writer.WriteControl(ControlSequences.SetForegroundColor(_nameColor.R, _nameColor.G, _nameColor.B)); | ||
writer.Write(_name); | ||
writer.WriteControl(ControlSequences.ResetAttributes()); | ||
writer.Write("]"); | ||
|
||
writer.Write("["); | ||
writer.WriteControl(ControlSequences.SetForegroundColor(_eventColor.R, _eventColor.G, _eventColor.B)); | ||
writer.Write(eventName); | ||
writer.WriteControl(ControlSequences.ResetAttributes()); | ||
writer.Write("] "); | ||
|
||
var hasMessage = string.IsNullOrWhiteSpace(message); | ||
|
||
if (hasMessage) | ||
writer.Write(message); | ||
|
||
if (exception != null) | ||
{ | ||
if (hasMessage) | ||
writer.WriteLine(); | ||
|
||
writer.Write(exception); | ||
} | ||
} | ||
} |
16 changes: 16 additions & 0 deletions
16
src/driver/Logging/TerminalLanguageServiceLoggerProvider.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
namespace Vezel.Celerity.Driver.Logging; | ||
|
||
internal sealed class TerminalLanguageServiceLoggerProvider : LanguageServiceLoggerProvider | ||
{ | ||
public TerminalWriter Writer { get; } | ||
|
||
public TerminalLanguageServiceLoggerProvider(TerminalWriter writer) | ||
{ | ||
Writer = writer; | ||
} | ||
|
||
public override TerminalLanguageServiceLogger CreateLogger(string name) | ||
{ | ||
return new(this, name); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
using Vezel.Celerity.Language.Service.Logging; | ||
|
||
namespace Vezel.Celerity.Language.Service; | ||
|
||
public sealed class LanguageService : IDisposable | ||
{ | ||
public Task Completion { get; } | ||
|
||
private readonly TaskCompletionSource _disposed = new(TaskCreationOptions.RunContinuationsAsynchronously); | ||
|
||
private readonly LanguageServer _server; | ||
|
||
private LanguageService(LanguageServer server) | ||
{ | ||
_server = server; | ||
Completion = Task.WhenAny(server.WaitForExit, _disposed.Task); | ||
} | ||
|
||
public static ValueTask<LanguageService> CreateAsync( | ||
LanguageServiceConfiguration configuration, CancellationToken cancellationToken = default) | ||
{ | ||
Check.Null(configuration); | ||
|
||
return CreateAsync(); | ||
|
||
[AsyncMethodBuilder(typeof(PoolingAsyncValueTaskMethodBuilder<>))] | ||
[SuppressMessage("", "CA2000")] | ||
async ValueTask<LanguageService> CreateAsync() | ||
{ | ||
T GetAttribute<T>() | ||
where T : Attribute | ||
{ | ||
#pragma warning disable CS0436 // TODO: https://github.com/dotnet/Nerdbank.GitVersioning/issues/555 | ||
return typeof(ThisAssembly).Assembly.GetCustomAttribute<T>()!; | ||
#pragma warning restore CS0436 | ||
} | ||
|
||
return new( | ||
await LanguageServer.From( | ||
new LanguageServerOptions() | ||
.WithServerInfo(new() | ||
{ | ||
Name = GetAttribute<AssemblyProductAttribute>()!.Product, | ||
Version = GetAttribute<AssemblyInformationalVersionAttribute>()!.InformationalVersion, | ||
}) | ||
.WithInput(configuration.Input) | ||
.WithOutput(configuration.Output) | ||
.WithContentModifiedSupport(true) | ||
.WithMaximumRequestTimeout(configuration.RequestTimeout) | ||
.ConfigureLogging(builder => | ||
{ | ||
_ = builder.SetMinimumLevel(configuration.LogLevel switch | ||
{ | ||
Logging.LogLevel.Trace => Microsoft.Extensions.Logging.LogLevel.Trace, | ||
Logging.LogLevel.Debug => Microsoft.Extensions.Logging.LogLevel.Debug, | ||
Logging.LogLevel.Information => Microsoft.Extensions.Logging.LogLevel.Information, | ||
Logging.LogLevel.Warning => Microsoft.Extensions.Logging.LogLevel.Warning, | ||
Logging.LogLevel.Error => Microsoft.Extensions.Logging.LogLevel.Error, | ||
Logging.LogLevel.Critical => Microsoft.Extensions.Logging.LogLevel.Critical, | ||
_ => throw new UnreachableException(), | ||
}); | ||
|
||
if (configuration.LoggerProvider is { } provider) | ||
_ = builder.AddProvider(new LanguageServiceLoggerProviderAdapter(provider)); | ||
|
||
if (configuration.ProtocolLogging) | ||
_ = builder.AddLanguageProtocolLogging(); | ||
}), | ||
cancellationToken).ConfigureAwait(false)); | ||
} | ||
} | ||
|
||
public void Dispose() | ||
{ | ||
_server.Dispose(); | ||
|
||
_ = _disposed.TrySetResult(); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,114 @@ | ||
using Vezel.Celerity.Language.Service.Logging; | ||
|
||
namespace Vezel.Celerity.Language.Service; | ||
|
||
public sealed class LanguageServiceConfiguration | ||
{ | ||
public Stream Input { get; private set; } | ||
|
||
public Stream Output { get; private set; } | ||
|
||
public TimeSpan RequestTimeout { get; private set; } = Timeout.InfiniteTimeSpan; | ||
|
||
public Logging.LogLevel LogLevel { get; private set; } = Logging.LogLevel.Information; | ||
|
||
public LanguageServiceLoggerProvider? LoggerProvider { get; private set; } | ||
|
||
public bool ProtocolLogging { get; private set; } | ||
|
||
private LanguageServiceConfiguration() | ||
{ | ||
Input = null!; | ||
Output = null!; | ||
} | ||
|
||
public LanguageServiceConfiguration(Stream input, Stream output) | ||
{ | ||
Check.Null(input); | ||
Check.Argument(input.CanRead, input); | ||
Check.Null(output); | ||
Check.Argument(output.CanWrite, output); | ||
|
||
Input = input; | ||
Output = output; | ||
} | ||
|
||
private LanguageServiceConfiguration Clone() | ||
{ | ||
return new() | ||
{ | ||
Input = Input, | ||
Output = Output, | ||
RequestTimeout = RequestTimeout, | ||
LogLevel = LogLevel, | ||
LoggerProvider = LoggerProvider, | ||
ProtocolLogging = ProtocolLogging, | ||
}; | ||
} | ||
|
||
public LanguageServiceConfiguration WithInput(Stream input) | ||
{ | ||
Check.Null(input); | ||
Check.Argument(input.CanRead, input); | ||
|
||
var cfg = Clone(); | ||
|
||
cfg.Input = input; | ||
|
||
return cfg; | ||
} | ||
|
||
public LanguageServiceConfiguration WithOutput(Stream output) | ||
{ | ||
Check.Null(output); | ||
Check.Argument(output.CanWrite, output); | ||
|
||
var cfg = Clone(); | ||
|
||
cfg.Output = output; | ||
|
||
return cfg; | ||
} | ||
|
||
public LanguageServiceConfiguration WithRequestTimeout(TimeSpan requestTimeout) | ||
{ | ||
Check.Range((long)requestTimeout.TotalMilliseconds is >= -1 and <= int.MaxValue, requestTimeout); | ||
|
||
var cfg = Clone(); | ||
|
||
cfg.RequestTimeout = requestTimeout; | ||
|
||
return cfg; | ||
} | ||
|
||
public LanguageServiceConfiguration WithLogLevel(Logging.LogLevel logLevel) | ||
{ | ||
Check.Enum(logLevel); | ||
|
||
var cfg = Clone(); | ||
|
||
cfg.LogLevel = logLevel; | ||
|
||
return cfg; | ||
} | ||
|
||
public LanguageServiceConfiguration WithLoggerProvider(LanguageServiceLoggerProvider loggerProvider) | ||
{ | ||
Check.Null(loggerProvider); | ||
|
||
var cfg = Clone(); | ||
|
||
cfg.LoggerProvider = loggerProvider; | ||
|
||
return cfg; | ||
} | ||
|
||
public LanguageServiceConfiguration WithProtocolLogging(bool protocolLogging) | ||
{ | ||
var cfg = Clone(); | ||
|
||
cfg.ProtocolLogging = protocolLogging; | ||
|
||
return cfg; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
namespace Vezel.Celerity.Language.Service.Logging; | ||
|
||
public abstract class LanguageServiceLogger | ||
{ | ||
public abstract void Log(LogLevel logLevel, string eventName, string message, Exception? exception); | ||
} |
Oops, something went wrong.