212 lines
8.6 KiB
C#
212 lines
8.6 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Text;
|
|
using System.Threading;
|
|
using System.Threading.Tasks;
|
|
using JsonRpc.Standard.Contracts;
|
|
using LanguageServer.VsCode;
|
|
using LanguageServer.VsCode.Contracts;
|
|
using Upsilon.Binder;
|
|
using Upsilon.Binder.VariableSymbols;
|
|
using Upsilon.BoundTypes;
|
|
using Upsilon.Utilities;
|
|
using Type = Upsilon.BaseTypes.Type;
|
|
|
|
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.GetBottomNodeAtPosition(linePos + position.Character);
|
|
if (findNode != null)
|
|
{
|
|
var contents = new StringBuilder($"Kind: {findNode.Kind}");
|
|
if (findNode is BoundExpression expression)
|
|
{
|
|
contents.Append($"\n\nType: {expression.Type}");
|
|
}
|
|
|
|
if (findNode is BoundVariableSymbol varSymbol)
|
|
{
|
|
if (varSymbol.VariableSymbol.CommentValue != null)
|
|
{
|
|
contents.Append($"\n\n{string.Join(" \n", varSymbol.VariableSymbol.CommentValue)}");
|
|
}
|
|
|
|
if (varSymbol.VariableSymbol is FunctionVariableSymbol fVar)
|
|
{
|
|
contents.Append($"\n\nReturns: {fVar.ResultType}");
|
|
}
|
|
}
|
|
else if (findNode is BoundFunctionExpression fe)
|
|
{
|
|
contents.Append($"\n\nReturns: {fe.ReturnType}");
|
|
}
|
|
|
|
return new Hover()
|
|
{
|
|
Contents = contents.ToString()
|
|
};
|
|
}
|
|
}
|
|
}
|
|
|
|
return new Hover();
|
|
}
|
|
|
|
[JsonRpcMethod]
|
|
public SignatureHelp SignatureHelp(TextDocumentIdentifier textDocument, Position position)
|
|
{
|
|
// TODO
|
|
return new SignatureHelp(new List<SignatureInformation>
|
|
{
|
|
});
|
|
}
|
|
|
|
[JsonRpcMethod(IsNotification = true)]
|
|
public async Task DidOpen(TextDocumentItem textDocument)
|
|
{
|
|
if (textDocument.Uri.IsUntitled())
|
|
{
|
|
var workspace = Session.Client.Workspace;
|
|
if (workspace != null)
|
|
{
|
|
|
|
}
|
|
}
|
|
|
|
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 _);
|
|
}
|
|
|
|
[JsonRpcMethod]
|
|
public async Task<CompletionList> Completion(TextDocumentIdentifier textDocument, Position position, CompletionContext context)
|
|
{
|
|
if (Session.Documents.TryGetValue(textDocument.Uri, out var doc) && doc.Bound != null)
|
|
{
|
|
var linePos = doc.SourceText.GetLineStartPos(position.Line);
|
|
var characterPosition = linePos + position.Character;
|
|
using (var nodeIterator = doc.Bound.GetNodeAtPosition(characterPosition).GetEnumerator())
|
|
{
|
|
if (nodeIterator.MoveNext())
|
|
{
|
|
var node = nodeIterator.Current;
|
|
if (node.Kind != BoundKind.BoundFullstopIndexExpression && nodeIterator.MoveNext())
|
|
{
|
|
node = nodeIterator.Current;
|
|
}
|
|
|
|
if (node is BoundFullStopIndexExpression indexExpression)
|
|
{
|
|
var expression = indexExpression.Expression;
|
|
var variableSymbol = Binder.ResolveVariable(expression, null);
|
|
var result = await GetListFromVariableSymbol(variableSymbol);
|
|
if (result != null)
|
|
{
|
|
return result;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
var scope = doc.Bound.GetScopeAt(characterPosition);
|
|
var variables = scope.GetBoundScopeVisibleVariables();
|
|
if (scope != null)
|
|
{
|
|
return new CompletionList(
|
|
variables.Select(x =>
|
|
{
|
|
var (key, value) = x;
|
|
return new CompletionItem(value.Name,
|
|
CompletionItemKind.Variable, value.Type.ToString(),
|
|
x.Value.CommentValue == null
|
|
? ""
|
|
: $"\n\n{string.Join(" \n", value.CommentValue)}");
|
|
}));
|
|
}
|
|
}
|
|
return new CompletionList(new CompletionItem[0], false);
|
|
}
|
|
|
|
private static async Task<CompletionList> GetListFromVariableSymbol(VariableSymbol variableSymbol)
|
|
{
|
|
if (variableSymbol is UserDataVariableSymbol parameterSymbol &&
|
|
parameterSymbol.BoundTypeDefinition is UserDataBoundTypeDefinition udBoundDef)
|
|
{
|
|
return new CompletionList(
|
|
udBoundDef.Properties.Select(x =>
|
|
{
|
|
var (key, value) = x;
|
|
return new CompletionItem(key, CompletionItemKind.Variable,
|
|
$"{value.ActualType}({value.Type})", $"{value.Comment}", null);
|
|
}));
|
|
}
|
|
if (variableSymbol is TableVariableSymbol tableSymbol)
|
|
{
|
|
return new CompletionList(
|
|
tableSymbol.Variables.Select(x =>
|
|
{
|
|
var (key, value) = x;
|
|
return new CompletionItem(key,
|
|
CompletionItemKind.Variable, value.Type.ToString(),
|
|
x.Value.CommentValue == null
|
|
? ""
|
|
: $"{string.Join(" \n", value.CommentValue)}", null);
|
|
}));
|
|
}
|
|
|
|
return null;
|
|
}
|
|
}
|
|
}
|