-
Notifications
You must be signed in to change notification settings - Fork 418
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[Alpha] Language Server Protocol #969
Changes from 23 commits
22531ca
d89bc7b
6ae34e9
1dbdeee
379fc46
f9af5e2
e619220
a5ad0d6
247ff2a
6619ea2
87cd734
93b3b6b
d08895c
78ef0e8
6616047
8ffc10d
5fcb2b7
0e507ef
7b9cabe
483fcb0
00cfbd5
76f4555
da2075c
d214876
88cd26e
9f8afd9
2a47bce
ee0a0c5
7a6b8c9
e14fd60
7ccd1d6
b995a63
f5c0f19
a349680
5dd4421
cc34932
bbe6137
801daa4
85da6f6
be45851
e88689b
62249dc
25cafe1
541be2e
b5c9475
e5a5459
b30d431
5d8195e
215d250
4a3531f
803dab7
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
using System.Runtime.CompilerServices; | ||
|
||
[assembly: InternalsVisibleTo("TestUtility")] | ||
[assembly: InternalsVisibleTo("OmniSharp")] | ||
[assembly: InternalsVisibleTo("OmniSharp.Stdio.Tests")] | ||
[assembly: InternalsVisibleTo("OmniSharp.LanguageServer.Tests")] |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
using System; | ||
using System.Linq; | ||
using Microsoft.Extensions.Logging; | ||
using Newtonsoft.Json; | ||
using OmniSharp.Eventing; | ||
using OmniSharp.Extensions.LanguageServer; | ||
using OmniSharp.Extensions.LanguageServer.Models; | ||
using OmniSharp.Extensions.LanguageServer.Protocol; | ||
using OmniSharp.Models.Diagnostics; | ||
using OmniSharp.Models.Events; | ||
using OmniSharp.Stdio.Protocol; | ||
using OmniSharp.Stdio.Services; | ||
|
||
namespace OmniSharp.LanguageServerProtocol.Eventing | ||
{ | ||
public class LanguageServerEventEmitter : IEventEmitter | ||
{ | ||
private readonly LanguageServer _server; | ||
|
||
public LanguageServerEventEmitter(LanguageServer server) | ||
{ | ||
_server = server; | ||
} | ||
|
||
public void Emit(string kind, object args) | ||
{ | ||
switch (kind) | ||
{ | ||
case EventTypes.Diagnostic: | ||
if (args is DiagnosticMessage message) | ||
{ | ||
var groups = message.Results | ||
// TODO: Make this is correct | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. what a funny comment! I don't know whether to ask you to correct the typo, or to correct the thing that needs correction. 😄 |
||
.GroupBy(z => Helpers.ToUri(z.FileName), z => z.QuickFixes); | ||
|
||
foreach (var group in groups) | ||
{ | ||
_server.PublishDiagnostics(new PublishDiagnosticsParams() | ||
{ | ||
Uri = group.Key, | ||
Diagnostics = group | ||
.SelectMany(z => z.Select(v => v.ToDiagnostic())) | ||
.ToArray() | ||
}); | ||
} | ||
} | ||
break; | ||
case EventTypes.ProjectAdded: | ||
case EventTypes.ProjectChanged: | ||
case EventTypes.ProjectRemoved: | ||
case EventTypes.Error: | ||
case EventTypes.PackageRestoreStarted: | ||
case EventTypes.PackageRestoreFinished: | ||
case EventTypes.UnresolvedDependencies: | ||
// TODO: | ||
break; | ||
} | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,144 @@ | ||
using System; | ||
using System.Collections.Generic; | ||
using System.Composition; | ||
using System.Linq; | ||
using System.Threading; | ||
using System.Threading.Tasks; | ||
using OmniSharp.Extensions.JsonRpc; | ||
using OmniSharp.Extensions.LanguageServer.Capabilities.Client; | ||
using OmniSharp.Extensions.LanguageServer.Models; | ||
using OmniSharp.Extensions.LanguageServer.Protocol; | ||
using OmniSharp.Mef; | ||
using OmniSharp.Models.AutoComplete; | ||
using OmniSharp.Models.TypeLookup; | ||
|
||
namespace OmniSharp.LanguageServerProtocol.Handlers | ||
{ | ||
[Shared, Export(typeof(CompletionHandler))] | ||
class CompletionHandler : ICompletionHandler | ||
{ | ||
public static IEnumerable<IJsonRpcHandler> Enumerate(RequestHandlers handlers) | ||
{ | ||
foreach (var (selector, handler) in handlers | ||
.OfType<Mef.IRequestHandler<AutoCompleteRequest, IEnumerable<AutoCompleteResponse>>>()) | ||
if (handler != null) | ||
yield return new CompletionHandler(handler, selector); | ||
} | ||
|
||
private CompletionCapability _capability; | ||
private readonly Mef.IRequestHandler<AutoCompleteRequest, IEnumerable<AutoCompleteResponse>> _autoCompleteHandler; | ||
private readonly DocumentSelector _documentSelector; | ||
|
||
private static readonly IDictionary<string, CompletionItemKind> _kind = new Dictionary<string, CompletionItemKind>{ | ||
// types | ||
{ "Class", CompletionItemKind.Class }, | ||
{ "Delegate", CompletionItemKind.Class }, // need a better option for this. | ||
{ "Enum", CompletionItemKind.Enum }, | ||
{ "Interface", CompletionItemKind.Interface }, | ||
{ "Struct", CompletionItemKind.Class }, // TODO: Is struct missing from enum? | ||
|
||
// variables | ||
{ "Local", CompletionItemKind.Variable }, | ||
{ "Parameter", CompletionItemKind.Variable }, | ||
{ "RangeVariable", CompletionItemKind.Variable }, | ||
|
||
// members | ||
{ "Const", CompletionItemKind.Value }, // TODO: Is const missing from enum? | ||
{ "EnumMember", CompletionItemKind.Enum }, | ||
{ "Event", CompletionItemKind.Function }, // TODO: Is event missing from enum? | ||
{ "Field", CompletionItemKind.Field }, | ||
{ "Method", CompletionItemKind.Method }, | ||
{ "Property", CompletionItemKind.Property }, | ||
|
||
// other stuff | ||
{ "Label", CompletionItemKind.Unit }, // need a better option for this. | ||
{ "Keyword", CompletionItemKind.Keyword }, | ||
{ "Namespace", CompletionItemKind.Module } | ||
}; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. FWIW, several of the missing items above have been added to VS Code's There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Looking at version 4 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yup! That was filed after I asked about LSP's |
||
|
||
private static CompletionItemKind GetCompletionItemKind(string key) | ||
{ | ||
if (string.IsNullOrEmpty(key)) | ||
{ | ||
return CompletionItemKind.Property; | ||
} | ||
if(_kind.TryGetValue(key, out var completionItemKind)) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: space. |
||
{ | ||
return completionItemKind; | ||
} | ||
return CompletionItemKind.Property; | ||
} | ||
|
||
[ImportingConstructor] | ||
public CompletionHandler(Mef.IRequestHandler<AutoCompleteRequest, IEnumerable<AutoCompleteResponse>> autoCompleteHandler, DocumentSelector documentSelector) | ||
{ | ||
_autoCompleteHandler = autoCompleteHandler; | ||
_documentSelector = documentSelector; | ||
} | ||
|
||
public async Task<CompletionList> Handle(TextDocumentPositionParams request, CancellationToken token) | ||
{ | ||
var omnisharpRequest = new AutoCompleteRequest() | ||
{ | ||
FileName = Helpers.FromUri(request.TextDocument.Uri), | ||
Column = Convert.ToInt32(request.Position.Character), | ||
Line = Convert.ToInt32(request.Position.Line), | ||
WantKind = true, | ||
WantDocumentationForEveryCompletionResult = true, | ||
WantReturnType = true | ||
}; | ||
|
||
var omnisharpResponse = await _autoCompleteHandler.Handle(omnisharpRequest); | ||
|
||
var completions = new Dictionary<string, List<CompletionItem>>(); | ||
foreach (var response in omnisharpResponse) | ||
{ | ||
var completionItem = new CompletionItem { | ||
Label = response.CompletionText, | ||
Detail = !string.IsNullOrEmpty(response.ReturnType) ? | ||
response.DisplayText : | ||
$"{response.ReturnType} {response.DisplayText}", | ||
Documentation = response.Description, | ||
Kind = GetCompletionItemKind(response.Kind), | ||
InsertText = response.CompletionText, | ||
}; | ||
|
||
if(!completions.ContainsKey(completionItem.Label)) | ||
{ | ||
completions[completionItem.Label] = new List<CompletionItem>(); | ||
} | ||
completions[completionItem.Label].Add(completionItem); | ||
} | ||
|
||
var result = new List<CompletionItem>(); | ||
foreach (var key in completions.Keys) | ||
{ | ||
var suggestion = completions[key][0]; | ||
var overloadCount = completions[key].Count - 1; | ||
|
||
if (overloadCount > 0) | ||
{ | ||
// indicate that there is more | ||
suggestion.Detail = $"{suggestion.Detail} (+ {overloadCount} overload(s))"; | ||
} | ||
|
||
result.Add(suggestion); | ||
} | ||
|
||
return new CompletionList(result); | ||
} | ||
|
||
public CompletionRegistrationOptions GetRegistrationOptions() | ||
{ | ||
return new CompletionRegistrationOptions() | ||
{ | ||
DocumentSelector = _documentSelector | ||
}; | ||
} | ||
|
||
public void SetCapability(CompletionCapability capability) | ||
{ | ||
_capability = capability; | ||
} | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should this be a case-insensitive comparison?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The command line stuff specifically uses lowercase, but I'll change in case that ever changes.