Skip to content

Commit

Permalink
Merge pull request #1877 from 333fred/completion-service
Browse files Browse the repository at this point in the history
Start moving omnisharp to directly using Roslyn's completion service, with a separate resolve step
  • Loading branch information
filipw authored Aug 17, 2020
2 parents 8e7eef8 + a22d6cb commit 90def5b
Show file tree
Hide file tree
Showing 27 changed files with 1,998 additions and 211 deletions.
136 changes: 136 additions & 0 deletions src/OmniSharp.Abstractions/Models/v1/Completion/CompletionItem.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
#nullable enable

using System.Collections.Immutable;

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>
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>
public CompletionItemKind Kind { get; set; }

/// <summary>
/// Tags for this completion item
/// </summary>
public ImmutableArray<CompletionItemTag>? Tags { get; set; }

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

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

/// <summary>
/// Select this item when showing.
/// </summary>
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>
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>
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>
public string? InsertText { get; set; }

/// <summary>
/// The format of <see cref="InsertText"/>.
/// </summary>
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>
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>
public ImmutableArray<LinePositionSpanTextChange>? AdditionalTextEdits { get; set; }

/// <summary>
/// Index in the completions list that this completion occurred.
/// </summary>
public int Data { get; set; }

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

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,9 @@
#nullable enable

namespace OmniSharp.Models.v1.Completion
{
public class CompletionResolveResponse
{
public CompletionItem? Item { get; set; }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
#nullable enable

using System.Collections.Immutable;

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>
public bool IsIncomplete { get; set; }

/// <summary>
/// The completion items.
/// </summary>
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

0 comments on commit 90def5b

Please sign in to comment.