Skip to content
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

Start moving omnisharp to directly using Roslyn's completion service, with a separate resolve step #1877

Merged
merged 6 commits into from
Aug 17, 2020
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
177 changes: 177 additions & 0 deletions src/OmniSharp.Abstractions/Models/v1/Completion/CompletionItem.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,177 @@
#nullable enable

using System.Collections.Immutable;
using Newtonsoft.Json;

namespace OmniSharp.Models.v1.Completion
{
public class CompletionItem
{
/// <summary>
/// The label of this completion item. By default
/// also the text that is inserted when selecting
/// this completion.
/// </summary>
[JsonProperty("label")]
public string Label { get; set; } = null!;

/// <summary>
/// The kind of this completion item. Based of the kind
/// an icon is chosen by the editor.The standardized set
/// of available values is defined in <see cref="CompletionItemKind"/>
/// </summary>
[JsonProperty("kind")]
public CompletionItemKind Kind { get; set; }

/// <summary>
/// Tags for this completion item
/// </summary>
[JsonProperty("tags")]
public ImmutableArray<CompletionItemTag>? Tags { get; set; }

/// <summary>
/// A human-readable string with additional information
/// about this item, like type or symbol information
/// </summary>
[JsonProperty("detail")]
public string? Detail { get; set; }

/// <summary>
/// A human-readable string that represents a doc-comment. This is
/// formatted as markdown.
/// </summary>
[JsonProperty("documentation")]
public string? Documentation { get; set; }

/// <summary>
/// Select this item when showing.
/// </summary>
[JsonProperty("preselect")]
public bool Preselect { get; set; }

/// <summary>
/// A string that should be used when comparing this item
/// with other items. When null or empty the label is used.
/// </summary>
[JsonProperty("sortText")]
public string? SortText { get; set; }

/// <summary>
/// A string that should be used when filtering a set of
/// completion items. When null or empty the label is used.
/// </summary>
[JsonProperty("filterText")]
public string? FilterText { get; set; }

/// <summary>
/// A string that should be inserted into a document when selecting
/// this completion.When null or empty the label is used.
/// </summary>
[JsonProperty("insertText")]
public string? InsertText { get; set; }

/// <summary>
/// The format of <see cref="InsertText"/>.
/// </summary>
[JsonProperty("insertTextFormat")]
public InsertTextFormat? InsertTextFormat { get; set; }

/// <summary>
/// An optional set of characters that when pressed while this completion is active will accept it first and
/// then type that character.
/// </summary>
[JsonProperty("commitCharacters")]
public ImmutableArray<char>? CommitCharacters { get; set; }

/// <summary>
/// An optional array of additional text edits that are applied when
/// selecting this completion.Edits must not overlap (including the same insert position)
/// with the main edit nor with themselves.
///
/// Additional text edits should be used to change text unrelated to the current cursor position
/// (for example adding an import statement at the top of the file if the completion item will
/// insert an unqualified type).
/// </summary>
[JsonProperty("additionalTextEdits")]
public ImmutableArray<TextEdit>? AdditionalTextEdits { get; set; }

/// <summary>
/// Index in the completions list that this completion occurred.
/// </summary>
[JsonProperty("data")]
public int Data { get; set; }

public override string ToString()
{
return $"{{ {nameof(Label)} = {Label}, {nameof(CompletionItemKind)} = {Kind} }}";
}
}

public struct TextEdit
333fred marked this conversation as resolved.
Show resolved Hide resolved
{
[JsonProperty("range")]
public Range Range { get; set; }

[JsonProperty("newText")]
public string? NewText { get; set; }
}

// These are using different versions from the normal Range/Point classes in order to apply
// json converters that match up with lsp naming conventions.
public struct Range
{
[JsonProperty("start")]
public Position Start { get; set; }
[JsonProperty("end")]
public Position End { get; set; }
}

public struct Position
{
[JsonProperty("line")]
public int Line { get; set; }
[JsonProperty("character")]
public int Character { get; set; }
}

public enum CompletionItemKind
{
Text = 1,
Method = 2,
Function = 3,
Constructor = 4,
Field = 5,
Variable = 6,
Class = 7,
Interface = 8,
Module = 9,
Property = 10,
Unit = 11,
Value = 12,
Enum = 13,
Keyword = 14,
Snippet = 15,
Color = 16,
File = 17,
Reference = 18,
Folder = 19,
EnumMember = 20,
Constant = 21,
Struct = 22,
Event = 23,
Operator = 24,
TypeParameter = 25,
}

public enum CompletionItemTag
{
Deprecated = 1,
}

public enum InsertTextFormat
{
PlainText = 1,
// TODO: Support snippets
Snippet = 2,
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
#nullable enable

using System.ComponentModel;
using OmniSharp.Mef;

namespace OmniSharp.Models.v1.Completion
{
[OmniSharpEndpoint(OmniSharpEndpoints.Completion, typeof(CompletionRequest), typeof(CompletionResponse))]
public class CompletionRequest : Request
{
/// <summary>
/// How the completion was triggered
/// </summary>
public CompletionTriggerKind CompletionTrigger { get; set; }

/// <summary>
/// The character that triggered completion if <see cref="CompletionTrigger"/>
/// is <see cref="CompletionTriggerKind.TriggerCharacter"/>. <see langword="null"/>
/// otherwise.
/// </summary>
public char? TriggerCharacter { get; set; }
}

public enum CompletionTriggerKind
{
/// <summary>
/// Completion was triggered by typing an identifier (24x7 code
/// complete), manual invocation (e.g Ctrl+Space) or via API
/// </summary>
Invoked = 1,
/// <summary>
/// Completion was triggered by a trigger character specified by
/// the `triggerCharacters` properties of the `CompletionRegistrationOptions`.
/// </summary>
TriggerCharacter = 2,

// We don't need to support incomplete completion lists that need to be recomputed
// later, but this is reserving the number to match LSP if we need it later.
[EditorBrowsable(EditorBrowsableState.Never)]
TriggerForIncompleteCompletions = 3
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
#nullable enable

using OmniSharp.Mef;

namespace OmniSharp.Models.v1.Completion
{
[OmniSharpEndpoint(OmniSharpEndpoints.CompletionResolve, typeof(CompletionResolveRequest), typeof(CompletionResolveResponse))]
public class CompletionResolveRequest : IRequest
{
public CompletionItem Item { get; set; } = null!;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
#nullable enable

using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;

namespace OmniSharp.Models.v1.Completion
{
public class CompletionResolveResponse
{
[JsonProperty("item")]
public CompletionItem? Item { get; set; }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
#nullable enable

using System.Collections.Generic;
using System.Collections.Immutable;
using Newtonsoft.Json;

namespace OmniSharp.Models.v1.Completion
{
public class CompletionResponse
{
/// <summary>
/// If true, this list is not complete. Further typing should result in recomputing the list.
/// </summary>
[JsonProperty("isIncomplete")]
public bool IsIncomplete { get; set; }

/// <summary>
/// The completion items.
/// </summary>
[JsonProperty("items")]
public ImmutableArray<CompletionItem> Items { get; set; }
}
}
3 changes: 3 additions & 0 deletions src/OmniSharp.Abstractions/OmniSharpEndpoints.cs
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,9 @@ public static class OmniSharpEndpoints
public const string ReAnalyze = "/reanalyze";
public const string QuickInfo = "/quickinfo";

public const string Completion = "/completion";
public const string CompletionResolve = "/completion/resolve";

public static class V2
{
public const string GetCodeActions = "/v2/getcodeactions";
Expand Down
19 changes: 19 additions & 0 deletions src/OmniSharp.Roslyn.CSharp/Helpers/LspSnippetHelpers.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
using System.Text.RegularExpressions;

namespace OmniSharp.Roslyn.CSharp.Helpers
{
public static class LspSnippetHelpers
{
private static Regex EscapeRegex = new Regex(@"([\\\$}])", RegexOptions.Compiled);

/// <summary>
/// Escape the given string for use as an LSP snippet. This escapes '\', '$', and '}'.
/// </summary>
public static string Escape(string snippet)
{
if (snippet == null)
return null;
return EscapeRegex.Replace(snippet, @"\$1");
}
}
}
Loading