Initial commit
This commit is contained in:
6
UpsilonLanguageServer/.idea/.idea.UpsilonLanguageServer/.idea/vcs.xml
generated
Normal file
6
UpsilonLanguageServer/.idea/.idea.UpsilonLanguageServer/.idea/vcs.xml
generated
Normal file
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="VcsDirectoryMappings">
|
||||
<mapping directory="$PROJECT_DIR$/.." vcs="Git" />
|
||||
</component>
|
||||
</project>
|
||||
BIN
UpsilonLanguageServer/Lib/System.Collections.Immutable.dll
Normal file
BIN
UpsilonLanguageServer/Lib/System.Collections.Immutable.dll
Normal file
Binary file not shown.
63
UpsilonLanguageServer/Lib/Upsilon.deps.json
Normal file
63
UpsilonLanguageServer/Lib/Upsilon.deps.json
Normal file
@@ -0,0 +1,63 @@
|
||||
{
|
||||
"runtimeTarget": {
|
||||
"name": ".NETStandard,Version=v2.0/",
|
||||
"signature": "7417c30610758d349c0e111cd15f02499f764b67"
|
||||
},
|
||||
"compilationOptions": {},
|
||||
"targets": {
|
||||
".NETStandard,Version=v2.0": {},
|
||||
".NETStandard,Version=v2.0/": {
|
||||
"Upsilon/1.0.0": {
|
||||
"dependencies": {
|
||||
"NETStandard.Library": "2.0.3",
|
||||
"System.Collections.Immutable": "1.5.0"
|
||||
},
|
||||
"runtime": {
|
||||
"Upsilon.dll": {}
|
||||
}
|
||||
},
|
||||
"Microsoft.NETCore.Platforms/1.1.0": {},
|
||||
"NETStandard.Library/2.0.3": {
|
||||
"dependencies": {
|
||||
"Microsoft.NETCore.Platforms": "1.1.0"
|
||||
}
|
||||
},
|
||||
"System.Collections.Immutable/1.5.0": {
|
||||
"runtime": {
|
||||
"lib/netstandard2.0/System.Collections.Immutable.dll": {
|
||||
"assemblyVersion": "1.2.3.0",
|
||||
"fileVersion": "4.6.26515.6"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"libraries": {
|
||||
"Upsilon/1.0.0": {
|
||||
"type": "project",
|
||||
"serviceable": false,
|
||||
"sha512": ""
|
||||
},
|
||||
"Microsoft.NETCore.Platforms/1.1.0": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-kz0PEW2lhqygehI/d6XsPCQzD7ff7gUJaVGPVETX611eadGsA3A877GdSlU0LRVMCTH/+P3o2iDTak+S08V2+A==",
|
||||
"path": "microsoft.netcore.platforms/1.1.0",
|
||||
"hashPath": "microsoft.netcore.platforms.1.1.0.nupkg.sha512"
|
||||
},
|
||||
"NETStandard.Library/2.0.3": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-st47PosZSHrjECdjeIzZQbzivYBJFv6P2nv4cj2ypdI204DO+vZ7l5raGMiX4eXMJ53RfOIg+/s4DHVZ54Nu2A==",
|
||||
"path": "netstandard.library/2.0.3",
|
||||
"hashPath": "netstandard.library.2.0.3.nupkg.sha512"
|
||||
},
|
||||
"System.Collections.Immutable/1.5.0": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-RGxi2aQoXgZ5ge0zxrKqI4PU9LrYYoLC+cnEnWXKsSduCOUhE1GEAAoTexUVT8RZOILQyy1B27HC8Xw/XLGzdQ==",
|
||||
"path": "system.collections.immutable/1.5.0",
|
||||
"hashPath": "system.collections.immutable.1.5.0.nupkg.sha512"
|
||||
}
|
||||
}
|
||||
}
|
||||
BIN
UpsilonLanguageServer/Lib/Upsilon.dll
Normal file
BIN
UpsilonLanguageServer/Lib/Upsilon.dll
Normal file
Binary file not shown.
BIN
UpsilonLanguageServer/Lib/Upsilon.pdb
Normal file
BIN
UpsilonLanguageServer/Lib/Upsilon.pdb
Normal file
Binary file not shown.
16
UpsilonLanguageServer/UpsilonLanguageServer.sln
Normal file
16
UpsilonLanguageServer/UpsilonLanguageServer.sln
Normal file
@@ -0,0 +1,16 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UpsilonLanguageServer", "UpsilonLanguageServer\UpsilonLanguageServer.csproj", "{E8C3C18A-F82B-4D8D-9E87-7BE03C0D6AF1}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Release|Any CPU = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{E8C3C18A-F82B-4D8D-9E87-7BE03C0D6AF1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{E8C3C18A-F82B-4D8D-9E87-7BE03C0D6AF1}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{E8C3C18A-F82B-4D8D-9E87-7BE03C0D6AF1}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{E8C3C18A-F82B-4D8D-9E87-7BE03C0D6AF1}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
@@ -0,0 +1,3 @@
|
||||
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
|
||||
<s:Boolean x:Key="/Default/CodeStyle/Naming/CSharpAutoNaming/IsNamingAutoDetectionCompleted/@EntryValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeStyle/Naming/CSharpAutoNaming/IsNotificationDisabled/@EntryValue">True</s:Boolean></wpf:ResourceDictionary>
|
||||
@@ -0,0 +1,90 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using LanguageServer.VsCode.Contracts;
|
||||
using LanguageServer.VsCode.Server;
|
||||
using Upsilon;
|
||||
using Upsilon.Evaluator;
|
||||
|
||||
namespace UpsilonLanguageServer
|
||||
{
|
||||
public class DiagnosticProvider
|
||||
{
|
||||
|
||||
public DiagnosticProvider()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
public static async Task<IEnumerable<Diagnostic>> LintDocument(TextDocument document, SessionDocument session,
|
||||
int maxNumberOfProblems)
|
||||
{
|
||||
var diag = new List<Diagnostic>();
|
||||
var content = document.Content;
|
||||
if (string.IsNullOrWhiteSpace(content))
|
||||
{
|
||||
diag.Add(new Diagnostic(DiagnosticSeverity.Hint,
|
||||
new Range(new Position(0, 0), document.PositionAt(content?.Length ?? 0)),
|
||||
"DLS", "DLS0001", "Empty document. Try typing something, such as \".net core\"."));
|
||||
return diag;
|
||||
}
|
||||
|
||||
var task = RealLint(document, maxNumberOfProblems, session, content, diag);
|
||||
// either wait until the script is linted, or until a second has passed (timeout)
|
||||
await Task.WhenAny(task, Task.Delay(1000));
|
||||
return diag;
|
||||
}
|
||||
|
||||
private static async Task RealLint(TextDocument document, int maxNumberOfProblems, SessionDocument session,
|
||||
string content, List<Diagnostic> diag)
|
||||
{
|
||||
try
|
||||
{
|
||||
using (var script = new Script(content))
|
||||
{
|
||||
session.SourceText = script.ScriptString;
|
||||
if (script.Diagnostics.Messages.Count > 0)
|
||||
{
|
||||
foreach (var error in script.Diagnostics.Messages)
|
||||
{
|
||||
diag.Add(ConvertToVsCodeDiagnostic(error));
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
var bound = script.Bind();
|
||||
foreach (var error in script.Diagnostics.Messages)
|
||||
{
|
||||
diag.Add(ConvertToVsCodeDiagnostic(error));
|
||||
}
|
||||
|
||||
if (script.Diagnostics.Messages.Count == 0)
|
||||
{
|
||||
session.Bound = bound;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
diag.Add(new Diagnostic(DiagnosticSeverity.Error,
|
||||
new Range(new Position(0, 0), document.PositionAt(content?.Length ?? 0)),
|
||||
"DLS", "DLS0001", e.ToString()));
|
||||
}
|
||||
}
|
||||
|
||||
private static Diagnostic ConvertToVsCodeDiagnostic(DiagnosticsMessage error)
|
||||
{
|
||||
var (startPosition, startPositionOnLine) = error.GetStartLine();
|
||||
var (endPosition, endPositionOnLine) = error.GetEndLine();
|
||||
var sourceString = error.Diagnostics.ScriptString;
|
||||
if (endPosition >= sourceString.GetLineCount())
|
||||
endPosition = sourceString.GetLineCount();
|
||||
|
||||
var range = new Range(startPosition, startPositionOnLine, endPosition,
|
||||
endPositionOnLine);
|
||||
return new Diagnostic(DiagnosticSeverity.Error, range, error.AtError(), "ERR001", error.Message);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,118 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using JsonRpc.DynamicProxy.Client;
|
||||
using JsonRpc.Standard.Client;
|
||||
using JsonRpc.Standard.Contracts;
|
||||
using LanguageServer.VsCode.Contracts;
|
||||
using LanguageServer.VsCode.Contracts.Client;
|
||||
using LanguageServer.VsCode.Server;
|
||||
using Upsilon.Binder;
|
||||
using Upsilon.Text;
|
||||
|
||||
namespace UpsilonLanguageServer
|
||||
{
|
||||
public class LanguageServerSession
|
||||
{
|
||||
private readonly CancellationTokenSource _cts = new CancellationTokenSource();
|
||||
|
||||
public LanguageServerSession(JsonRpcClient rpcClient, IJsonRpcContractResolver contractResolver)
|
||||
{
|
||||
RpcClient = rpcClient ?? throw new ArgumentNullException(nameof(rpcClient));
|
||||
var builder = new JsonRpcProxyBuilder {ContractResolver = contractResolver};
|
||||
Client = new ClientProxy(builder, rpcClient);
|
||||
Documents = new ConcurrentDictionary<Uri, SessionDocument>();
|
||||
DiagnosticProvider = new DiagnosticProvider();
|
||||
}
|
||||
|
||||
public CancellationToken CancellationToken => _cts.Token;
|
||||
|
||||
public JsonRpcClient RpcClient { get; }
|
||||
|
||||
public ClientProxy Client { get; }
|
||||
|
||||
public ConcurrentDictionary<Uri, SessionDocument> Documents { get; }
|
||||
|
||||
public DiagnosticProvider DiagnosticProvider { get; }
|
||||
|
||||
public LanguageServerSettings Settings { get; set; } = new LanguageServerSettings();
|
||||
|
||||
public void StopServer()
|
||||
{
|
||||
_cts.Cancel();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public class SessionDocument
|
||||
{
|
||||
/// <summary>
|
||||
/// Actually makes the changes to the inner document per this milliseconds.
|
||||
/// </summary>
|
||||
private const int RenderChangesDelay = 100;
|
||||
|
||||
public SessionDocument(TextDocumentItem doc)
|
||||
{
|
||||
Document = TextDocument.Load<FullTextDocument>(doc);
|
||||
}
|
||||
|
||||
private Task _updateChangesDelayTask;
|
||||
|
||||
private readonly object _syncLock = new object();
|
||||
|
||||
private List<TextDocumentContentChangeEvent> _impendingChanges = new List<TextDocumentContentChangeEvent>();
|
||||
|
||||
public event EventHandler DocumentChanged;
|
||||
|
||||
public TextDocument Document { get; set; }
|
||||
|
||||
public BoundScript Bound { get; set; }
|
||||
public SourceText SourceText { get; set; }
|
||||
|
||||
public void NotifyChanges(IEnumerable<TextDocumentContentChangeEvent> changes)
|
||||
{
|
||||
lock (_syncLock)
|
||||
{
|
||||
if (_impendingChanges == null)
|
||||
_impendingChanges = changes.ToList();
|
||||
else
|
||||
_impendingChanges.AddRange(changes);
|
||||
}
|
||||
if (_updateChangesDelayTask == null || _updateChangesDelayTask.IsCompleted)
|
||||
{
|
||||
_updateChangesDelayTask = Task.Delay(RenderChangesDelay);
|
||||
_updateChangesDelayTask.ContinueWith(t => Task.Run((Action)MakeChanges));
|
||||
}
|
||||
}
|
||||
|
||||
private void MakeChanges()
|
||||
{
|
||||
List<TextDocumentContentChangeEvent> localChanges;
|
||||
lock (_syncLock)
|
||||
{
|
||||
localChanges = _impendingChanges;
|
||||
if (localChanges == null || localChanges.Count == 0) return;
|
||||
_impendingChanges = null;
|
||||
}
|
||||
Document = Document.ApplyChanges(localChanges);
|
||||
if (_impendingChanges == null)
|
||||
{
|
||||
localChanges.Clear();
|
||||
lock (_syncLock)
|
||||
{
|
||||
if (_impendingChanges == null)
|
||||
_impendingChanges = localChanges;
|
||||
}
|
||||
}
|
||||
OnDocumentChanged();
|
||||
}
|
||||
|
||||
protected virtual void OnDocumentChanged()
|
||||
{
|
||||
DocumentChanged?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
}
|
||||
}
|
||||
104
UpsilonLanguageServer/UpsilonLanguageServer/Program.cs
Normal file
104
UpsilonLanguageServer/UpsilonLanguageServer/Program.cs
Normal file
@@ -0,0 +1,104 @@
|
||||
// #define WAIT_FOR_DEBUGGER
|
||||
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Threading;
|
||||
using JsonRpc.Standard.Client;
|
||||
using JsonRpc.Standard.Contracts;
|
||||
using JsonRpc.Standard.Server;
|
||||
using JsonRpc.Streams;
|
||||
using LanguageServer.VsCode;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Logging.Debug;
|
||||
|
||||
namespace UpsilonLanguageServer
|
||||
{
|
||||
static class Program
|
||||
{
|
||||
static void Main(string[] args)
|
||||
{
|
||||
var debugMode = args.Any(a => a.Equals("--debug", StringComparison.OrdinalIgnoreCase));
|
||||
#if WAIT_FOR_DEBUGGER
|
||||
while (!Debugger.IsAttached) Thread.Sleep(1000);
|
||||
Debugger.Break();
|
||||
#endif
|
||||
StreamWriter logWriter = null;
|
||||
if (debugMode)
|
||||
{
|
||||
logWriter = File.CreateText("messages-" + DateTime.Now.ToString("yyyyMMddHHmmss") + ".log");
|
||||
logWriter.AutoFlush = true;
|
||||
}
|
||||
using (logWriter)
|
||||
using (var cin = Console.OpenStandardInput())
|
||||
using (var bcin = new BufferedStream(cin))
|
||||
using (var cout = Console.OpenStandardOutput())
|
||||
using (var reader = new PartwiseStreamMessageReader(bcin))
|
||||
using (var writer = new PartwiseStreamMessageWriter(cout))
|
||||
{
|
||||
var contractResolver = new JsonRpcContractResolver
|
||||
{
|
||||
NamingStrategy = new CamelCaseJsonRpcNamingStrategy(),
|
||||
ParameterValueConverter = new CamelCaseJsonValueConverter(),
|
||||
};
|
||||
var clientHandler = new StreamRpcClientHandler();
|
||||
var client = new JsonRpcClient(clientHandler);
|
||||
if (debugMode)
|
||||
{
|
||||
// We want to capture log all the LSP server-to-client calls as well
|
||||
clientHandler.MessageSending += (_, e) =>
|
||||
{
|
||||
lock (logWriter) logWriter.WriteLine("{0} <C{1}", Utility.GetTimeStamp(), e.Message);
|
||||
};
|
||||
clientHandler.MessageReceiving += (_, e) =>
|
||||
{
|
||||
lock (logWriter) logWriter.WriteLine("{0} >C{1}", Utility.GetTimeStamp(), e.Message);
|
||||
};
|
||||
}
|
||||
// Configure & build service host
|
||||
var session = new LanguageServerSession(client, contractResolver);
|
||||
var host = BuildServiceHost(logWriter, contractResolver, debugMode);
|
||||
var serverHandler = new StreamRpcServerHandler(host,
|
||||
StreamRpcServerHandlerOptions.ConsistentResponseSequence |
|
||||
StreamRpcServerHandlerOptions.SupportsRequestCancellation);
|
||||
serverHandler.DefaultFeatures.Set(session);
|
||||
// If we want server to stop, just stop the "source"
|
||||
using (serverHandler.Attach(reader, writer))
|
||||
using (clientHandler.Attach(reader, writer))
|
||||
{
|
||||
// Wait for the "stop" request.
|
||||
session.CancellationToken.WaitHandle.WaitOne();
|
||||
}
|
||||
logWriter?.WriteLine("Exited");
|
||||
}
|
||||
}
|
||||
|
||||
private static IJsonRpcServiceHost BuildServiceHost(TextWriter logWriter,
|
||||
IJsonRpcContractResolver contractResolver, bool debugMode)
|
||||
{
|
||||
var loggerFactory = new LoggerFactory();
|
||||
loggerFactory.AddProvider(new DebugLoggerProvider(null));
|
||||
var builder = new JsonRpcServiceHostBuilder
|
||||
{
|
||||
ContractResolver = contractResolver,
|
||||
LoggerFactory = loggerFactory
|
||||
};
|
||||
builder.UseCancellationHandling();
|
||||
builder.Register(typeof(Program).GetTypeInfo().Assembly);
|
||||
if (debugMode)
|
||||
{
|
||||
// Log all the client-to-server calls.
|
||||
builder.Intercept(async (context, next) =>
|
||||
{
|
||||
lock (logWriter) logWriter.WriteLine("{0} > {1}", Utility.GetTimeStamp(), context.Request);
|
||||
await next();
|
||||
lock (logWriter) logWriter.WriteLine("{0} < {1}", Utility.GetTimeStamp(), context.Response);
|
||||
});
|
||||
}
|
||||
return builder.Build();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
using JsonRpc.Standard.Contracts;
|
||||
using LanguageServer.VsCode.Contracts;
|
||||
|
||||
namespace UpsilonLanguageServer.Services
|
||||
{
|
||||
[JsonRpcScope(MethodPrefix = "completionItem/")]
|
||||
public class CompletionItemService : UpsilonLanguageServiceBase
|
||||
{
|
||||
// The request is sent from the client to the server to resolve additional information
|
||||
// for a given completion item.
|
||||
[JsonRpcMethod(AllowExtensionData = true)]
|
||||
public CompletionItem Resolve()
|
||||
{
|
||||
var item = RequestContext.Request.Parameters.ToObject<CompletionItem>(Utility.CamelCaseJsonSerializer);
|
||||
// Add a pair of square brackets around the inserted text.
|
||||
item.InsertText = $"[{item.Label}]";
|
||||
return item;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using JsonRpc.Standard;
|
||||
using JsonRpc.Standard.Contracts;
|
||||
using JsonRpc.Standard.Server;
|
||||
using LanguageServer.VsCode.Contracts;
|
||||
using Newtonsoft.Json.Linq;
|
||||
// ReSharper disable UnusedMember.Global
|
||||
|
||||
namespace UpsilonLanguageServer.Services
|
||||
{
|
||||
public class InitializationService : UpsilonLanguageServiceBase
|
||||
{
|
||||
|
||||
[JsonRpcMethod(AllowExtensionData = true)]
|
||||
public InitializeResult Initialize(int processId, Uri rootUri, ClientCapabilities capabilities,
|
||||
JToken initializationOptions = null, string trace = null)
|
||||
{
|
||||
return new InitializeResult(new ServerCapabilities
|
||||
{
|
||||
HoverProvider = true,
|
||||
SignatureHelpProvider = new SignatureHelpOptions("()"),
|
||||
CompletionProvider = new CompletionOptions(true, "."),
|
||||
TextDocumentSync = new TextDocumentSyncOptions
|
||||
{
|
||||
OpenClose = true,
|
||||
WillSave = true,
|
||||
Change = TextDocumentSyncKind.Incremental
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
[JsonRpcMethod(IsNotification = true)]
|
||||
public async Task Initialized()
|
||||
{
|
||||
await Client.Window.ShowMessage(MessageType.Info, "Upsilon Language Server Initialized");
|
||||
}
|
||||
|
||||
[JsonRpcMethod]
|
||||
public void Shutdown()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
[JsonRpcMethod(IsNotification = true)]
|
||||
public void Exit()
|
||||
{
|
||||
Client.Window.ShowMessage(MessageType.Info, "Upsilon Language Server exited");
|
||||
Session.StopServer();
|
||||
}
|
||||
|
||||
[JsonRpcMethod("$/cancelRequest", IsNotification = true)]
|
||||
public void CancelRequest(MessageId id)
|
||||
{
|
||||
RequestContext.Features.Get<IRequestCancellationFeature>().TryCancel(id);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,123 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using JsonRpc.Standard.Contracts;
|
||||
using LanguageServer.VsCode;
|
||||
using LanguageServer.VsCode.Contracts;
|
||||
using LanguageServer.VsCode.Server;
|
||||
using Upsilon.Binder;
|
||||
|
||||
namespace UpsilonLanguageServer.Services
|
||||
{
|
||||
[JsonRpcScope(MethodPrefix = "textDocument/")]
|
||||
public class TextDocumentService : UpsilonLanguageServiceBase
|
||||
{
|
||||
[JsonRpcMethod]
|
||||
public async Task<Hover> Hover(TextDocumentIdentifier textDocument, Position position, CancellationToken ct)
|
||||
{
|
||||
// Note that Hover is cancellable.
|
||||
//await Task.Delay(1000, ct);
|
||||
if (Session.Documents.TryGetValue(textDocument.Uri, out var doc))
|
||||
{
|
||||
if (doc.Bound != null && doc.SourceText != null)
|
||||
{
|
||||
var linePos = doc.SourceText.GetLineStartPos(position.Line);
|
||||
var findNode = doc.Bound.GetNodeAtPosition(linePos + position.Character);
|
||||
if (findNode != null)
|
||||
{
|
||||
var contents = $"Kind: {findNode.Kind}";
|
||||
if (findNode is BoundExpression expression)
|
||||
{
|
||||
contents += $"\n\nType: {expression.Type}";
|
||||
}
|
||||
|
||||
return new Hover()
|
||||
{
|
||||
Contents = contents
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return new Hover();
|
||||
}
|
||||
|
||||
[JsonRpcMethod]
|
||||
public SignatureHelp SignatureHelp(TextDocumentIdentifier textDocument, Position position)
|
||||
{
|
||||
return new SignatureHelp(new List<SignatureInformation>
|
||||
{
|
||||
new SignatureInformation("**Function1**", "Documentation1"),
|
||||
new SignatureInformation("**Function2** <strong>test</strong>", "Documentation2"),
|
||||
});
|
||||
}
|
||||
|
||||
[JsonRpcMethod(IsNotification = true)]
|
||||
public async Task DidOpen(TextDocumentItem textDocument)
|
||||
{
|
||||
var doc = new SessionDocument(textDocument);
|
||||
var session = Session;
|
||||
doc.DocumentChanged += async (sender, args) =>
|
||||
{
|
||||
// Lint the document when it's changed.
|
||||
var sessionDocument = ((SessionDocument) sender);
|
||||
var doc1 = sessionDocument.Document;
|
||||
var diag1 = DiagnosticProvider.LintDocument(doc1, sessionDocument, session.Settings.MaxNumberOfProblems);
|
||||
if (session.Documents.ContainsKey(doc1.Uri))
|
||||
{
|
||||
// In case the document has been closed when we were linting…
|
||||
await session.Client.Document.PublishDiagnostics(doc1.Uri, await diag1);
|
||||
}
|
||||
};
|
||||
Session.Documents.TryAdd(textDocument.Uri, doc);
|
||||
var diag = DiagnosticProvider.LintDocument(doc.Document, doc, Session.Settings.MaxNumberOfProblems);
|
||||
await Client.Document.PublishDiagnostics(textDocument.Uri, await diag);
|
||||
}
|
||||
|
||||
[JsonRpcMethod(IsNotification = true)]
|
||||
public void DidChange(TextDocumentIdentifier textDocument,
|
||||
ICollection<TextDocumentContentChangeEvent> contentChanges)
|
||||
{
|
||||
Session.Documents[textDocument.Uri].NotifyChanges(contentChanges);
|
||||
}
|
||||
|
||||
[JsonRpcMethod(IsNotification = true)]
|
||||
public void WillSave(TextDocumentIdentifier textDocument, TextDocumentSaveReason reason)
|
||||
{
|
||||
//Client.Window.LogMessage(MessageType.Log, "-----------");
|
||||
//Client.Window.LogMessage(MessageType.Log, Documents[textDocument].Content);
|
||||
}
|
||||
|
||||
[JsonRpcMethod(IsNotification = true)]
|
||||
public async Task DidClose(TextDocumentIdentifier textDocument)
|
||||
{
|
||||
if (textDocument.Uri.IsUntitled())
|
||||
{
|
||||
await Client.Document.PublishDiagnostics(textDocument.Uri, new Diagnostic[0]);
|
||||
}
|
||||
Session.Documents.TryRemove(textDocument.Uri, out _);
|
||||
}
|
||||
|
||||
private static readonly CompletionItem[] PredefinedCompletionItems =
|
||||
{
|
||||
new CompletionItem(".NET", CompletionItemKind.Keyword,
|
||||
"Keyword1",
|
||||
"Short for **.NET Framework**, a software framework by Microsoft (possibly its subsets) or later open source .NET Core.",
|
||||
null),
|
||||
new CompletionItem(".NET Standard", CompletionItemKind.Keyword,
|
||||
"Keyword2",
|
||||
"The .NET Standard is a formal specification of .NET APIs that are intended to be available on all .NET runtimes.",
|
||||
null),
|
||||
new CompletionItem(".NET Framework", CompletionItemKind.Keyword,
|
||||
"Keyword3",
|
||||
".NET Framework (pronounced dot net) is a software framework developed by Microsoft that runs primarily on Microsoft Windows.", null),
|
||||
};
|
||||
|
||||
[JsonRpcMethod]
|
||||
public async Task<CompletionList> Completion(TextDocumentIdentifier textDocument, Position position, CompletionContext context)
|
||||
{
|
||||
return new CompletionList(PredefinedCompletionItems, false);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
using System;
|
||||
using JsonRpc.Standard.Server;
|
||||
using LanguageServer.VsCode.Contracts;
|
||||
using LanguageServer.VsCode.Contracts.Client;
|
||||
using LanguageServer.VsCode.Server;
|
||||
|
||||
namespace UpsilonLanguageServer.Services
|
||||
{
|
||||
public class UpsilonLanguageServiceBase : JsonRpcService
|
||||
{
|
||||
protected LanguageServerSession Session => RequestContext.Features.Get<LanguageServerSession>();
|
||||
|
||||
protected ClientProxy Client => Session.Client;
|
||||
|
||||
protected TextDocument GetDocument(Uri uri)
|
||||
{
|
||||
if (Session.Documents.TryGetValue(uri, out var sd))
|
||||
return sd.Document;
|
||||
return null;
|
||||
}
|
||||
|
||||
protected TextDocument GetDocument(TextDocumentIdentifier id) => GetDocument(id.Uri);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
using JsonRpc.Standard.Contracts;
|
||||
using LanguageServer.VsCode.Contracts;
|
||||
|
||||
namespace UpsilonLanguageServer.Services
|
||||
{
|
||||
[JsonRpcScope(MethodPrefix = "workspace/")]
|
||||
public class WorkspaceService : UpsilonLanguageServiceBase
|
||||
{
|
||||
[JsonRpcMethod(IsNotification = true)]
|
||||
public async Task DidChangeConfiguration(SettingsRoot settings)
|
||||
{
|
||||
Session.Settings = settings.UpsilonLanguageServer;
|
||||
foreach (var doc in Session.Documents.Values)
|
||||
{
|
||||
var diag = await DiagnosticProvider.LintDocument(doc.Document, doc, Session.Settings.MaxNumberOfProblems);
|
||||
await Client.Document.PublishDiagnostics(doc.Document.Uri, diag);
|
||||
}
|
||||
}
|
||||
|
||||
[JsonRpcMethod(IsNotification = true)]
|
||||
public async Task DidChangeWatchedFiles(ICollection<FileEvent> changes)
|
||||
{
|
||||
foreach (var change in changes)
|
||||
{
|
||||
if (!change.Uri.IsFile) continue;
|
||||
var localPath = change.Uri.AbsolutePath;
|
||||
if (string.Equals(Path.GetExtension(localPath), ".yup", StringComparison.InvariantCultureIgnoreCase))
|
||||
{
|
||||
// If the file has been removed, we will clear the lint result about it.
|
||||
// Note that pass null to PublishDiagnostics may mess up the client.
|
||||
if (change.Type == FileChangeType.Deleted)
|
||||
{
|
||||
await Client.Document.PublishDiagnostics(change.Uri, new Diagnostic[0]);
|
||||
}
|
||||
}
|
||||
else if (string.Equals(Path.GetExtension(localPath), ".lua", StringComparison.InvariantCultureIgnoreCase))
|
||||
{
|
||||
// If the file has been removed, we will clear the lint result about it.
|
||||
// Note that pass null to PublishDiagnostics may mess up the client.
|
||||
if (change.Type == FileChangeType.Deleted)
|
||||
{
|
||||
await Client.Document.PublishDiagnostics(change.Uri, new Diagnostic[0]);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
18
UpsilonLanguageServer/UpsilonLanguageServer/Settings.cs
Normal file
18
UpsilonLanguageServer/UpsilonLanguageServer/Settings.cs
Normal file
@@ -0,0 +1,18 @@
|
||||
namespace UpsilonLanguageServer
|
||||
{
|
||||
public class SettingsRoot
|
||||
{
|
||||
public LanguageServerSettings UpsilonLanguageServer { get; set; }
|
||||
}
|
||||
|
||||
public class LanguageServerSettings
|
||||
{
|
||||
public int MaxNumberOfProblems { get; set; } = 10;
|
||||
|
||||
public LanguageServerTraceSettings Trace { get; } = new LanguageServerTraceSettings();
|
||||
}
|
||||
|
||||
public class LanguageServerTraceSettings
|
||||
{
|
||||
public string Server { get; set; }
|
||||
}}
|
||||
@@ -0,0 +1,24 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>netcoreapp2.1</TargetFramework>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="CXuesong.JsonRpc.Streams" Version="0.4.2" />
|
||||
<PackageReference Include="CXuesong.LanguageServer.VsCode" Version="0.2.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging" Version="2.1.1" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="1.1.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.TraceSource" Version="2.1.1" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Reference Include="System.Collections.Immutable, Version=1.2.3.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
|
||||
<HintPath>..\Lib\System.Collections.Immutable.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Upsilon, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null">
|
||||
<HintPath>..\Lib\Upsilon.dll</HintPath>
|
||||
</Reference>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
19
UpsilonLanguageServer/UpsilonLanguageServer/Utility.cs
Normal file
19
UpsilonLanguageServer/UpsilonLanguageServer/Utility.cs
Normal file
@@ -0,0 +1,19 @@
|
||||
using System;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Serialization;
|
||||
|
||||
namespace UpsilonLanguageServer
|
||||
{
|
||||
public static class Utility
|
||||
{
|
||||
public static readonly JsonSerializer CamelCaseJsonSerializer = new JsonSerializer
|
||||
{
|
||||
ContractResolver = new CamelCasePropertyNamesContractResolver()
|
||||
};
|
||||
|
||||
public static string GetTimeStamp()
|
||||
{
|
||||
return DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff");
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user