Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
Original file line number Diff line number Diff line change
Expand Up @@ -492,11 +492,12 @@ Microsoft.DotNet.Interactive.Events
public System.String Code { get;}
public System.String ToString()
public class CompletionItem
.ctor(System.String displayText, System.String kind, System.String filterText = null, System.String sortText = null, System.String insertText = null, System.String documentation = null)
.ctor(System.String displayText, System.String kind, System.String filterText = null, System.String sortText = null, System.String insertText = null, System.Nullable<InsertTextFormat> insertTextFormat = null, System.String documentation = null)
public System.String DisplayText { get;}
public System.String Documentation { get; set;}
public System.String FilterText { get;}
public System.String InsertText { get;}
public System.Nullable<InsertTextFormat> InsertTextFormat { get;}
public System.String Kind { get;}
public System.String SortText { get;}
public System.String ToString()
Expand Down Expand Up @@ -532,6 +533,9 @@ Microsoft.DotNet.Interactive.Events
public Microsoft.DotNet.Interactive.LinePositionSpan LinePositionSpan { get;}
public class IncompleteCodeSubmissionReceived : KernelEvent
.ctor(Microsoft.DotNet.Interactive.Commands.SubmitCode submitCode)
public enum InsertTextFormat : System.Enum, System.IComparable, System.IConvertible, System.IFormattable
PlainText=1
Snippet=2
public abstract class KernelEvent
public Microsoft.DotNet.Interactive.Commands.KernelCommand Command { get;}
public System.String ToString()
Expand Down
30 changes: 27 additions & 3 deletions src/Microsoft.DotNet.Interactive.CSharp/CompletionExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -51,13 +51,37 @@ public static string GetKind(this RoslynCompletionItem completionItem)

public static CompletionItem ToModel(this RoslynCompletionItem item, RoslynCompletionDescription description)
{
var isGeneric =
item.Properties.TryGetValue("IsGeneric", out var isGenericProperty) &&
bool.TryParse(isGenericProperty, out var isGenericResult) &&
isGenericResult;

var isMethod =
item.Tags.Contains(WellKnownTags.Method) ||
item.Tags.Contains(WellKnownTags.ExtensionMethod);

var (displayTextSuffix, insertTextSuffix) = (isGeneric, isMethod) switch
{
(true, true) => ("<>", "<$1>($2)"),
(true, false) => ("<>", "<$1>"),
(false, true) => ("", "($1)"),
(false, false) => ("", ""),
};

var displayText = item.DisplayText + displayTextSuffix;
var insertText = item.FilterText + insertTextSuffix;

InsertTextFormat? insertTextFormat = isGeneric || isMethod
? InsertTextFormat.Snippet
: null;
return new CompletionItem(
displayText: item.DisplayText,
displayText: displayText,
kind: item.GetKind(),
filterText: item.FilterText,
sortText: item.SortText,
insertText: item.FilterText,
insertText: insertText,
insertTextFormat: insertTextFormat,
documentation: description.Text);
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,13 @@ public async Task HandleAsync(RequestCompletions command, KernelInvocationContex
var updatedWorkspace = await GetWorkspaceWithCode(command.Code, position);
var request = new Protocol.WorkspaceRequest(updatedWorkspace, _buffer.Id);
var completionResult = await _workspaceServer.GetCompletionList(request, new Budget());
var completionItems = completionResult.Items.Select(item => new CompletionItem(item.DisplayText, item.Kind, item.FilterText, item.SortText, item.InsertText, item.Documentation?.Value)).ToList();
var completionItems = completionResult.Items.Select(item => new CompletionItem(
displayText: item.DisplayText,
kind: item.Kind,
filterText: item.FilterText,
sortText: item.SortText,
insertText: item.InsertText,
documentation: item.Documentation?.Value)).ToList();

context.Publish(new CompletionsProduced(completionItems, command));
}
Expand Down
8 changes: 7 additions & 1 deletion src/Microsoft.DotNet.Interactive.FSharp/FSharpKernel.fs
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,13 @@ type FSharpKernel () as this =
let kind = getKindString declarationItem.Glyph
let filterText = getFilterText declarationItem
let! documentation = getDocumentation declarationItem
return CompletionItem(declarationItem.Name, kind, filterText=filterText, documentation=documentation, insertText=declarationItem.NameInCode)
let isMethod =
match declarationItem.Kind with
| CompletionItemKind.Method _ -> true
| _ -> false
let insertTextSuffix = if isMethod then "($1)" else ""
let insertTextFormat = if isMethod then System.Nullable(InsertTextFormat.Snippet) else System.Nullable<InsertTextFormat>()
return CompletionItem(declarationItem.Name, kind, filterText=filterText, documentation=documentation, insertText=declarationItem.NameInCode + insertTextSuffix, insertTextFormat=insertTextFormat)
}

let getDiagnostic (error: FSharpDiagnostic) =
Expand Down
12 changes: 6 additions & 6 deletions src/Microsoft.DotNet.Interactive.SqlServer/ToolsServiceClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -139,12 +139,12 @@ public async Task<IEnumerable<CompletionItem>> ProvideCompletionItemsAsync(Uri f
return sqlCompletionItems.Select(item =>
{
return new CompletionItem(
item.Label,
item.Kind is not null ? Enum.GetName(typeof(SqlCompletionItemKind), item.Kind) : string.Empty,
item.FilterText,
item.SortText,
item.InsertText,
item.Documentation);
displayText: item.Label,
kind: item.Kind is not null ? Enum.GetName(typeof(SqlCompletionItemKind), item.Kind) : string.Empty,
filterText: item.FilterText,
sortText: item.SortText,
insertText: item.InsertText,
documentation: item.Documentation);
});
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -459,5 +459,153 @@ public async Task fsharp_completions_can_read_doc_comments_from_nuget_packages()
.Should()
.ContainSingle(ci => !string.IsNullOrEmpty(ci.Documentation) && ci.Documentation.Contains("Represents JavaScript's null as a string. This field is read-only."));
}

[Theory]
[InlineData(Language.CSharp)]
[InlineData(Language.FSharp)]
public async Task Property_completions_are_returned_as_plain_text(Language language)
{
var kernel = CreateKernel(language);

var markupCode = "Console.Ou$$";

MarkupTestFile.GetLineAndColumn(markupCode, out var code, out var line, out var character);

await kernel.SendAsync(new RequestCompletions(code, new LinePosition(line, character)));

KernelEvents
.Should()
.ContainSingle<CompletionsProduced>()
.Which
.Completions
.Should()
.Contain(item =>
item.DisplayText == "Out" &&
item.InsertText == "Out" &&
item.InsertTextFormat == null);
}

[Theory]
[InlineData(Language.CSharp)]
[InlineData(Language.FSharp)]
public async Task Method_completions_are_returned_as_a_snippet(Language language)
{
var kernel = CreateKernel(language);

var markupCode = "Console.Wri$$";

MarkupTestFile.GetLineAndColumn(markupCode, out var code, out var line, out var character);

await kernel.SendAsync(new RequestCompletions(code, new LinePosition(line, character)));

KernelEvents
.Should()
.ContainSingle<CompletionsProduced>()
.Which
.Completions
.Should()
.Contain(item =>
item.DisplayText == "WriteLine" &&
item.InsertText == "WriteLine($1)" &&
item.InsertTextFormat == InsertTextFormat.Snippet);
}

[Fact]
public async Task FSharp_module_functions_are_returned_as_plain_text()
{
var kernel = CreateKernel(Language.FSharp);

var markupCode = "[1;2;3] |> List.ma$$";

MarkupTestFile.GetLineAndColumn(markupCode, out var code, out var line, out var character);

await kernel.SendAsync(new RequestCompletions(code, new LinePosition(line, character)));

KernelEvents
.Should()
.ContainSingle<CompletionsProduced>()
.Which
.Completions
.Should()
.Contain(item =>
item.DisplayText == "map" &&
item.InsertText == "map" &&
item.InsertTextFormat == null);
}

[Fact]
public async Task CSharp_generic_method_completions_are_returned_as_a_snippet()
{
// in general F# prefers to infer generic types, not specify them

var kernel = CreateKernel(Language.CSharp);

var markupCode = "System.Array.Emp$$";

MarkupTestFile.GetLineAndColumn(markupCode, out var code, out var line, out var character);

await kernel.SendAsync(new RequestCompletions(code, new LinePosition(line, character)));

KernelEvents
.Should()
.ContainSingle<CompletionsProduced>()
.Which
.Completions
.Should()
.Contain(item =>
item.DisplayText == "Empty<>" &&
item.InsertText == "Empty<$1>($2)" &&
item.InsertTextFormat == InsertTextFormat.Snippet);
}

[Theory]
[InlineData(Language.CSharp)]
[InlineData(Language.FSharp)]
public async Task Non_generic_type_completions_are_returned_as_plain_text(Language language)
{
var kernel = CreateKernel(language);

var markupCode = "System.Cons$$";

MarkupTestFile.GetLineAndColumn(markupCode, out var code, out var line, out var character);

await kernel.SendAsync(new RequestCompletions(code, new LinePosition(line, character)));

KernelEvents
.Should()
.ContainSingle<CompletionsProduced>()
.Which
.Completions
.Should()
.Contain(item =>
item.DisplayText == "Console" &&
item.InsertText == "Console" &&
item.InsertTextFormat == null);
}

[Fact]
public async Task CSharp_generic_type_completions_are_returned_as_a_snippet()
{
// in general F# prefers to infer generic types, not specify them

var kernel = CreateKernel(Language.CSharp);

var markupCode = "System.Collections.Generic.IEnu$$";

MarkupTestFile.GetLineAndColumn(markupCode, out var code, out var line, out var character);

await kernel.SendAsync(new RequestCompletions(code, new LinePosition(line, character)));

KernelEvents
.Should()
.ContainSingle<CompletionsProduced>()
.Which
.Completions
.Should()
.Contain(item =>
item.DisplayText == "IEnumerable<>" &&
item.InsertText == "IEnumerable<$1>" &&
item.InsertTextFormat == InsertTextFormat.Snippet);
}
}
}
Original file line number Diff line number Diff line change
@@ -1 +1 @@
{"event":{"linePositionSpan":null,"completions":[{"displayText":"WriteLine","kind":"Method","filterText":"WriteLine","sortText":"WriteLine","insertText":"WriteLine","documentation":"Writes the line"}]},"eventType":"CompletionsProduced","command":{"token":"the-token","id":"command-id","commandType":"RequestCompletions","command":{"code":"Console.Wri","linePosition":{"line":0,"character":11},"targetKernelName":null},"originUri":"","destinationUri":""}}
{"event":{"linePositionSpan":null,"completions":[{"displayText":"WriteLine","kind":"Method","filterText":"WriteLine","sortText":"WriteLine","insertText":"WriteLine","insertTextFormat":"snippet","documentation":"Writes the line"}]},"eventType":"CompletionsProduced","command":{"token":"the-token","id":"command-id","commandType":"RequestCompletions","command":{"code":"Console.Wri","linePosition":{"line":0,"character":11},"targetKernelName":null},"originUri":"","destinationUri":""}}
Original file line number Diff line number Diff line change
Expand Up @@ -291,12 +291,13 @@ IEnumerable<KernelEvent> events()
new[]
{
new CompletionItem(
"WriteLine",
"Method",
"WriteLine",
"WriteLine",
"WriteLine",
"Writes the line")
displayText: "WriteLine",
kind: "Method",
filterText: "WriteLine",
sortText: "WriteLine",
insertText: "WriteLine",
insertTextFormat: InsertTextFormat.Snippet,
documentation: "Writes the line")
},
requestCompletion);

Expand Down
5 changes: 4 additions & 1 deletion src/Microsoft.DotNet.Interactive/Events/CompletionItem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,14 @@ namespace Microsoft.DotNet.Interactive.Events
{
public class CompletionItem
{
public CompletionItem(string displayText, string kind, string filterText = null, string sortText = null, string insertText = null, string documentation = null)
public CompletionItem(string displayText, string kind, string filterText = null, string sortText = null, string insertText = null, InsertTextFormat? insertTextFormat = null, string documentation = null)
{
DisplayText = displayText ?? throw new ArgumentNullException(nameof(displayText));
Kind = kind ?? throw new ArgumentException(nameof(kind));
FilterText = filterText;
SortText = sortText;
InsertText = insertText;
InsertTextFormat = insertTextFormat;
Documentation = documentation;
}

Expand All @@ -27,6 +28,8 @@ public CompletionItem(string displayText, string kind, string filterText = null,

public string InsertText { get; }

public InsertTextFormat? InsertTextFormat { get; }

public string Documentation { get; set; }

public override string ToString() => DisplayText;
Expand Down
8 changes: 8 additions & 0 deletions src/Microsoft.DotNet.Interactive/Events/InsertTextFormat.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
namespace Microsoft.DotNet.Interactive.Events
{
public enum InsertTextFormat
{
PlainText = 1,
Snippet = 2,
}
}
3 changes: 2 additions & 1 deletion src/dotnet-interactive-vscode-common/src/languageProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,12 @@ export class CompletionItemProvider implements vscode.CompletionItemProvider {
}
const completionItems: Array<vscode.CompletionItem> = [];
for (const item of result.completions) {
const insertText: string | vscode.SnippetString = item.insertTextFormat === 'snippet' ? new vscode.SnippetString(item.insertText) : item.insertText;
const vscodeItem: vscode.CompletionItem = {
label: item.displayText,
documentation: item.documentation,
filterText: item.filterText,
insertText: item.insertText,
insertText: insertText,
sortText: item.sortText,
range: range,
kind: this.mapCompletionItem(item.kind)
Expand Down
6 changes: 6 additions & 0 deletions src/microsoft-dotnet-interactive/src/contracts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -381,9 +381,15 @@ export interface CompletionItem {
filterText: string;
sortText: string;
insertText: string;
insertTextFormat?: InsertTextFormat;
documentation: string;
}

export enum InsertTextFormat {
PlainText = "plaintext",
Snippet = "snippet",
}

export interface Diagnostic {
linePositionSpan: LinePositionSpan;
severity: DiagnosticSeverity;
Expand Down