Skip to content

Commit db9617a

Browse files
author
msftbot[bot]
authored
Merge pull request #51618 from dotnet/merges/master-to-master-vs-deps
Merge master to master-vs-deps
2 parents 994410c + 7464573 commit db9617a

File tree

9 files changed

+94
-36
lines changed

9 files changed

+94
-36
lines changed

src/Features/Core/Portable/Completion/CommonCompletionItem.cs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,8 @@ public static CompletionItem Create(
2525
bool showsWarningIcon = false,
2626
ImmutableDictionary<string, string> properties = null,
2727
ImmutableArray<string> tags = default,
28-
string inlineDescription = null)
28+
string inlineDescription = null,
29+
bool isComplexTextEdit = false)
2930
{
3031
tags = tags.NullToEmpty();
3132

@@ -54,7 +55,8 @@ public static CompletionItem Create(
5455
properties: properties,
5556
tags: tags,
5657
rules: rules,
57-
inlineDescription: inlineDescription);
58+
inlineDescription: inlineDescription,
59+
isComplexTextEdit: isComplexTextEdit);
5860
}
5961

6062
public static bool HasDescription(CompletionItem item)

src/Features/Core/Portable/Completion/CompletionItem.cs

Lines changed: 54 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,21 @@ public sealed class CompletionItem : IComparable<CompletionItem>
8484
/// </summary>
8585
public CompletionItemRules Rules { get; }
8686

87+
/// <summary>
88+
/// Returns true if this item's text edit requires complex resolution that
89+
/// may impact performance. For example, an edit may be complex if it needs
90+
/// to format or type check the resulting code, or make complex non-local
91+
/// changes to other parts of the file.
92+
/// Complex resolution is used so we only do the minimum amount of work
93+
/// needed to display completion items. It is performed only for the
94+
/// committed item just prior to commit. Thus, it is ideal for any expensive
95+
/// completion work that does not affect the display of the item in the
96+
/// completion list, but is necessary for committing the item.
97+
/// An example of an item type requiring complex resolution is C#/VB
98+
/// override completion.
99+
/// </summary>
100+
public bool IsComplexTextEdit { get; }
101+
87102
/// <summary>
88103
/// The name of the <see cref="CompletionProvider"/> that created this
89104
/// <see cref="CompletionItem"/>. Not available to clients. Only used by
@@ -110,7 +125,8 @@ private CompletionItem(
110125
CompletionItemRules rules,
111126
string displayTextPrefix,
112127
string displayTextSuffix,
113-
string inlineDescription)
128+
string inlineDescription,
129+
bool isComplexTextEdit)
114130
{
115131
DisplayText = displayText ?? "";
116132
DisplayTextPrefix = displayTextPrefix ?? "";
@@ -121,6 +137,7 @@ private CompletionItem(
121137
Properties = properties ?? ImmutableDictionary<string, string>.Empty;
122138
Tags = tags.NullToEmpty();
123139
Rules = rules ?? CompletionItemRules.Default;
140+
IsComplexTextEdit = isComplexTextEdit;
124141

125142
if (!DisplayText.Equals(filterText, StringComparison.Ordinal))
126143
{
@@ -154,6 +171,23 @@ public static CompletionItem Create(
154171
return Create(displayText, filterText, sortText, properties, tags, rules, displayTextPrefix, displayTextSuffix, inlineDescription: null);
155172
}
156173

174+
// binary back compat overload
175+
public static CompletionItem Create(
176+
string displayText,
177+
string filterText,
178+
string sortText,
179+
ImmutableDictionary<string, string> properties,
180+
ImmutableArray<string> tags,
181+
CompletionItemRules rules,
182+
string displayTextPrefix,
183+
string displayTextSuffix,
184+
string inlineDescription)
185+
{
186+
return Create(
187+
displayText, filterText, sortText, properties, tags, rules, displayTextPrefix,
188+
displayTextSuffix, inlineDescription, isComplexTextEdit: false);
189+
}
190+
157191
public static CompletionItem Create(
158192
string displayText,
159193
string filterText = null,
@@ -163,7 +197,8 @@ public static CompletionItem Create(
163197
CompletionItemRules rules = null,
164198
string displayTextPrefix = null,
165199
string displayTextSuffix = null,
166-
string inlineDescription = null)
200+
string inlineDescription = null,
201+
bool isComplexTextEdit = false)
167202
{
168203
return new CompletionItem(
169204
span: default,
@@ -175,7 +210,8 @@ public static CompletionItem Create(
175210
rules: rules,
176211
displayTextPrefix: displayTextPrefix,
177212
displayTextSuffix: displayTextSuffix,
178-
inlineDescription: inlineDescription);
213+
inlineDescription: inlineDescription,
214+
isComplexTextEdit: isComplexTextEdit);
179215
}
180216

181217
/// <summary>
@@ -210,7 +246,8 @@ public static CompletionItem Create(
210246
rules: rules,
211247
displayTextPrefix: null,
212248
displayTextSuffix: null,
213-
inlineDescription: null);
249+
inlineDescription: null,
250+
isComplexTextEdit: false);
214251
}
215252

216253
private CompletionItem With(
@@ -223,7 +260,8 @@ private CompletionItem With(
223260
Optional<CompletionItemRules> rules = default,
224261
Optional<string> displayTextPrefix = default,
225262
Optional<string> displayTextSuffix = default,
226-
Optional<string> inlineDescription = default)
263+
Optional<string> inlineDescription = default,
264+
Optional<bool> isComplexTextEdit = default)
227265
{
228266
var newSpan = span.HasValue ? span.Value : Span;
229267
var newDisplayText = displayText.HasValue ? displayText.Value : DisplayText;
@@ -235,6 +273,7 @@ private CompletionItem With(
235273
var newRules = rules.HasValue ? rules.Value : Rules;
236274
var newDisplayTextPrefix = displayTextPrefix.HasValue ? displayTextPrefix.Value : DisplayTextPrefix;
237275
var newDisplayTextSuffix = displayTextSuffix.HasValue ? displayTextSuffix.Value : DisplayTextSuffix;
276+
var newIsComplexTextEdit = isComplexTextEdit.HasValue ? isComplexTextEdit.Value : IsComplexTextEdit;
238277

239278
if (newSpan == Span &&
240279
newDisplayText == DisplayText &&
@@ -245,7 +284,8 @@ private CompletionItem With(
245284
newRules == Rules &&
246285
newDisplayTextPrefix == DisplayTextPrefix &&
247286
newDisplayTextSuffix == DisplayTextSuffix &&
248-
newInlineDescription == InlineDescription)
287+
newInlineDescription == InlineDescription &&
288+
newIsComplexTextEdit == IsComplexTextEdit)
249289
{
250290
return this;
251291
}
@@ -260,7 +300,8 @@ private CompletionItem With(
260300
rules: newRules,
261301
displayTextPrefix: newDisplayTextPrefix,
262302
displayTextSuffix: newDisplayTextSuffix,
263-
inlineDescription: newInlineDescription)
303+
inlineDescription: newInlineDescription,
304+
isComplexTextEdit: newIsComplexTextEdit)
264305
{
265306
AutomationText = AutomationText,
266307
ProviderName = ProviderName,
@@ -350,6 +391,12 @@ public CompletionItem AddTag(string tag)
350391
public CompletionItem WithRules(CompletionItemRules rules)
351392
=> With(rules: rules);
352393

394+
/// <summary>
395+
/// Creates a copy of this <see cref="CompletionItem"/> with the <see cref="IsComplexTextEdit"/> property changed.
396+
/// </summary>
397+
public CompletionItem WithIsComplexTextEdit(bool isComplexTextEdit)
398+
=> With(isComplexTextEdit: isComplexTextEdit);
399+
353400
private string _entireDisplayText;
354401

355402
int IComparable<CompletionItem>.CompareTo(CompletionItem other)

src/Features/Core/Portable/Completion/Providers/MemberInsertingCompletionItem.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,8 @@ public static CompletionItem Create(
3434
symbols: ImmutableArray.Create(symbol),
3535
contextPosition: descriptionPosition,
3636
properties: props,
37-
rules: rules);
37+
rules: rules,
38+
isComplexTextEdit: true);
3839
}
3940

4041
public static Task<CompletionDescription> GetDescriptionAsync(CompletionItem item, Document document, CancellationToken cancellationToken)

src/Features/Core/Portable/Completion/Providers/SymbolCompletionItem.cs

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,8 @@ private static CompletionItem CreateWorker(
3333
string filterText = null,
3434
SupportedPlatformData supportedPlatforms = null,
3535
ImmutableDictionary<string, string> properties = null,
36-
ImmutableArray<string> tags = default)
36+
ImmutableArray<string> tags = default,
37+
bool isComplexTextEdit = false)
3738
{
3839
var props = properties ?? ImmutableDictionary<string, string>.Empty;
3940

@@ -54,7 +55,8 @@ private static CompletionItem CreateWorker(
5455
glyph: firstSymbol.GetGlyph(),
5556
showsWarningIcon: supportedPlatforms != null,
5657
properties: props,
57-
tags: tags);
58+
tags: tags,
59+
isComplexTextEdit: isComplexTextEdit);
5860

5961
item = WithSupportedPlatforms(item, supportedPlatforms);
6062
return symbolEncoder(symbols, item);
@@ -267,7 +269,8 @@ public static CompletionItem CreateWithSymbolId(
267269
string filterText = null,
268270
SupportedPlatformData supportedPlatforms = null,
269271
ImmutableDictionary<string, string> properties = null,
270-
ImmutableArray<string> tags = default)
272+
ImmutableArray<string> tags = default,
273+
bool isComplexTextEdit = false)
271274
{
272275
return CreateWithSymbolId(
273276
displayText,
@@ -280,7 +283,8 @@ public static CompletionItem CreateWithSymbolId(
280283
filterText,
281284
supportedPlatforms,
282285
properties,
283-
tags);
286+
tags,
287+
isComplexTextEdit);
284288
}
285289

286290
public static CompletionItem CreateWithSymbolId(
@@ -294,12 +298,13 @@ public static CompletionItem CreateWithSymbolId(
294298
string filterText = null,
295299
SupportedPlatformData supportedPlatforms = null,
296300
ImmutableDictionary<string, string> properties = null,
297-
ImmutableArray<string> tags = default)
301+
ImmutableArray<string> tags = default,
302+
bool isComplexTextEdit = false)
298303
{
299304
return CreateWorker(
300305
displayText, displayTextSuffix, symbols, rules, contextPosition,
301306
s_addSymbolEncoding, sortText, insertionText,
302-
filterText, supportedPlatforms, properties, tags);
307+
filterText, supportedPlatforms, properties, tags, isComplexTextEdit);
303308
}
304309

305310
public static CompletionItem CreateWithNameAndKind(
@@ -313,12 +318,13 @@ public static CompletionItem CreateWithNameAndKind(
313318
string filterText = null,
314319
SupportedPlatformData supportedPlatforms = null,
315320
ImmutableDictionary<string, string> properties = null,
316-
ImmutableArray<string> tags = default)
321+
ImmutableArray<string> tags = default,
322+
bool isComplexTextEdit = false)
317323
{
318324
return CreateWorker(
319325
displayText, displayTextSuffix, symbols, rules, contextPosition,
320326
s_addSymbolInfo, sortText, insertionText,
321-
filterText, supportedPlatforms, properties, tags);
327+
filterText, supportedPlatforms, properties, tags, isComplexTextEdit);
322328
}
323329

324330
internal static string GetSymbolName(CompletionItem item)

src/Features/Core/Portable/PublicAPI.Shipped.txt

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -246,7 +246,6 @@ static Microsoft.CodeAnalysis.Completion.CompletionChange.Create(Microsoft.CodeA
246246
static Microsoft.CodeAnalysis.Completion.CompletionChange.Create(System.Collections.Immutable.ImmutableArray<Microsoft.CodeAnalysis.Text.TextChange> textChanges, int? newPosition = null, bool includesCommitCharacter = false) -> Microsoft.CodeAnalysis.Completion.CompletionChange
247247
static Microsoft.CodeAnalysis.Completion.CompletionDescription.Create(System.Collections.Immutable.ImmutableArray<Microsoft.CodeAnalysis.TaggedText> taggedParts) -> Microsoft.CodeAnalysis.Completion.CompletionDescription
248248
static Microsoft.CodeAnalysis.Completion.CompletionDescription.FromText(string text) -> Microsoft.CodeAnalysis.Completion.CompletionDescription
249-
static Microsoft.CodeAnalysis.Completion.CompletionItem.Create(string displayText, string filterText = null, string sortText = null, System.Collections.Immutable.ImmutableDictionary<string, string> properties = null, System.Collections.Immutable.ImmutableArray<string> tags = default(System.Collections.Immutable.ImmutableArray<string>), Microsoft.CodeAnalysis.Completion.CompletionItemRules rules = null, string displayTextPrefix = null, string displayTextSuffix = null, string inlineDescription = null) -> Microsoft.CodeAnalysis.Completion.CompletionItem
250249
static Microsoft.CodeAnalysis.Completion.CompletionItem.Create(string displayText, string filterText, string sortText, Microsoft.CodeAnalysis.Text.TextSpan span, System.Collections.Immutable.ImmutableDictionary<string, string> properties, System.Collections.Immutable.ImmutableArray<string> tags, Microsoft.CodeAnalysis.Completion.CompletionItemRules rules) -> Microsoft.CodeAnalysis.Completion.CompletionItem
251250
static Microsoft.CodeAnalysis.Completion.CompletionItem.Create(string displayText, string filterText, string sortText, System.Collections.Immutable.ImmutableDictionary<string, string> properties, System.Collections.Immutable.ImmutableArray<string> tags, Microsoft.CodeAnalysis.Completion.CompletionItemRules rules) -> Microsoft.CodeAnalysis.Completion.CompletionItem
252251
static Microsoft.CodeAnalysis.Completion.CompletionItem.Create(string displayText, string filterText, string sortText, System.Collections.Immutable.ImmutableDictionary<string, string> properties, System.Collections.Immutable.ImmutableArray<string> tags, Microsoft.CodeAnalysis.Completion.CompletionItemRules rules, string displayTextPrefix, string displayTextSuffix) -> Microsoft.CodeAnalysis.Completion.CompletionItem
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,6 @@
11
const Microsoft.CodeAnalysis.TextTags.Record = "Record" -> string
2+
Microsoft.CodeAnalysis.Completion.CompletionItem.IsComplexTextEdit.get -> bool
3+
Microsoft.CodeAnalysis.Completion.CompletionItem.WithIsComplexTextEdit(bool isComplexTextEdit) -> Microsoft.CodeAnalysis.Completion.CompletionItem
24
static Microsoft.CodeAnalysis.Completion.CompletionChange.Create(Microsoft.CodeAnalysis.Text.TextChange textChange, System.Collections.Immutable.ImmutableArray<Microsoft.CodeAnalysis.Text.TextChange> textChanges, int? newPosition = null, bool includesCommitCharacter = false) -> Microsoft.CodeAnalysis.Completion.CompletionChange
5+
static Microsoft.CodeAnalysis.Completion.CompletionItem.Create(string displayText, string filterText = null, string sortText = null, System.Collections.Immutable.ImmutableDictionary<string, string> properties = null, System.Collections.Immutable.ImmutableArray<string> tags = default(System.Collections.Immutable.ImmutableArray<string>), Microsoft.CodeAnalysis.Completion.CompletionItemRules rules = null, string displayTextPrefix = null, string displayTextSuffix = null, string inlineDescription = null, bool isComplexTextEdit = false) -> Microsoft.CodeAnalysis.Completion.CompletionItem
6+
static Microsoft.CodeAnalysis.Completion.CompletionItem.Create(string displayText, string filterText, string sortText, System.Collections.Immutable.ImmutableDictionary<string, string> properties, System.Collections.Immutable.ImmutableArray<string> tags, Microsoft.CodeAnalysis.Completion.CompletionItemRules rules, string displayTextPrefix, string displayTextSuffix, string inlineDescription) -> Microsoft.CodeAnalysis.Completion.CompletionItem

src/Features/LanguageServer/Protocol/Handler/Completion/CompletionHandler.cs

Lines changed: 4 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -215,13 +215,6 @@ static async Task<TCompletionItem> CreateCompletionItemAsync<TCompletionItem>(
215215
var completeDisplayText = stringBuilder.ToString();
216216
stringBuilder.Clear();
217217

218-
// The TextEdits for override and partial method completions are provided in the resolve handler.
219-
// We do not provide them in this handler.
220-
// HACK: We should not be accessing the completion item's properties directly.
221-
// See https://github.com/dotnet/roslyn/issues/51396.
222-
item.Properties.TryGetValue("Modifiers", out var modifier);
223-
var isOverrideOrPartialMethodCompletion = modifier != null && (modifier.Contains("Override") || modifier.Contains("Partial"));
224-
225218
var completionItem = new TCompletionItem
226219
{
227220
Label = completeDisplayText,
@@ -239,13 +232,13 @@ static async Task<TCompletionItem> CreateCompletionItemAsync<TCompletionItem>(
239232
Preselect = item.Rules.SelectionBehavior == CompletionItemSelectionBehavior.HardSelection,
240233
};
241234

242-
// Override and partial method completions are always populated in the resolve handler, so we
243-
// leave both TextEdit and InsertText unpopulated in these cases.
244-
if (isOverrideOrPartialMethodCompletion)
235+
// Complex text edits (e.g. override and partial method completions) are always populated in the
236+
// resolve handler, so we leave both TextEdit and InsertText unpopulated in these cases.
237+
if (item.IsComplexTextEdit)
245238
{
246239
// Razor C# is currently the only language client that supports LSP.InsertTextFormat.Snippet.
247240
// We can enable it for regular C# once LSP is used for local completion.
248-
if (snippetsSupported && clientName == ProtocolConstants.RazorCSharp)
241+
if (snippetsSupported)
249242
{
250243
completionItem.InsertTextFormat = LSP.InsertTextFormat.Snippet;
251244
}

src/Features/LanguageServer/Protocol/Handler/Completion/CompletionResolveHandler.cs

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -92,13 +92,16 @@ private static CompletionResolveData GetCompletionResolveData(LSP.CompletionItem
9292
.Select(tp => new ClassifiedTextRun(tp.Tag.ToClassificationTypeName(), tp.Text)));
9393
}
9494

95-
// We compute the TextEdit resolves for override and partial method completions here.
96-
// Lazily resolving TextEdits is technically a violation of the LSP spec, but is
97-
// currently supported by the VS client anyway. Once the VS client adheres to the spec,
98-
// this logic will need to change and VS will need to provide official support for
99-
// TextEdit resolution in some form.
100-
if (completionItem.InsertText == null && completionItem.TextEdit == null)
95+
// We compute the TextEdit resolves for complex text edits (e.g. override and partial
96+
// method completions) here. Lazily resolving TextEdits is technically a violation of
97+
// the LSP spec, but is currently supported by the VS client anyway. Once the VS client
98+
// adheres to the spec, this logic will need to change and VS will need to provide
99+
// official support for TextEdit resolution in some form.
100+
if (selectedItem.IsComplexTextEdit)
101101
{
102+
Contract.ThrowIfTrue(completionItem.InsertText != null);
103+
Contract.ThrowIfTrue(completionItem.TextEdit != null);
104+
102105
var snippetsSupported = context.ClientCapabilities.TextDocument?.Completion?.CompletionItem?.SnippetSupport ?? false;
103106

104107
completionItem.TextEdit = await GenerateTextEditAsync(

src/Features/LanguageServer/ProtocolUnitTests/Completion/CompletionResolveTests.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ void M()
5353
}
5454

5555
[Fact]
56+
[WorkItem(51125, "https://github.com/dotnet/roslyn/issues/51125")]
5657
public async Task TestResolveOverridesCompletionItemAsync()
5758
{
5859
var markup =
@@ -88,6 +89,7 @@ class B : A
8889
}
8990

9091
[Fact]
92+
[WorkItem(51125, "https://github.com/dotnet/roslyn/issues/51125")]
9193
public async Task TestResolveOverridesCompletionItem_SnippetsEnabledAsync()
9294
{
9395
var markup =
@@ -138,6 +140,7 @@ class B : A
138140
}
139141

140142
[Fact]
143+
[WorkItem(51125, "https://github.com/dotnet/roslyn/issues/51125")]
141144
public async Task TestResolveOverridesCompletionItem_SnippetsEnabled_CaretOutOfSnippetScopeAsync()
142145
{
143146
var markup =

0 commit comments

Comments
 (0)