forked from OmniSharp/omnisharp-roslyn
-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
The recent completion rewrite makes completion faster for a large number of cases, but it has a couple of issues: * Any completion provider that needs to make edits beyond the few we've special cased doesn't work correctly. Roslyn is looking to add more of these, such as dotnet/roslyn#47511. * Completion providers that we do special case can be _slow_. Override and partial method completion in big types is painful, taking over a minute to show up sometimes. To resolve this, I've added support for async edits by adding a new completion end point: /completion/afterInsert. It takes the item that was inserted, the file, and the new cursor position. It relies on the InsertText that was inserted still allowing for resolving the completion in the same way as before, so I had to force a few providers that don't behave well here to be eagerly resolved, regardless. I also kept import completion using the standard /completion/resolve step to resolve extra edits, as they don't need to modify the current cursor location at all.
- Loading branch information
Showing
15 changed files
with
1,251 additions
and
247 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
namespace System.Runtime.CompilerServices | ||
{ | ||
internal sealed class IsExternalInit { } | ||
} |
12 changes: 12 additions & 0 deletions
12
src/OmniSharp.Abstractions/Models/v1/Completion/CompletionAfterInsertRequest.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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.CompletionAfterInsert, typeof(CompletionAfterInsertRequest), typeof(CompletionAfterInsertResponse))] | ||
public class CompletionAfterInsertRequest : IRequest | ||
{ | ||
public CompletionItem Item { get; set; } = null!; | ||
} | ||
} |
26 changes: 26 additions & 0 deletions
26
src/OmniSharp.Abstractions/Models/v1/Completion/CompletionAfterInsertResponse.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
#nullable enable | ||
|
||
using Newtonsoft.Json; | ||
using System.Collections.Generic; | ||
|
||
namespace OmniSharp.Models.v1.Completion | ||
{ | ||
public class CompletionAfterInsertResponse | ||
{ | ||
/// <summary> | ||
/// Text changes to be applied to the document. These need to be applied in batch, all with reference to | ||
/// the same original document. | ||
/// </summary> | ||
public IReadOnlyList<LinePositionSpanTextChange>? Changes { get; set; } | ||
/// <summary> | ||
/// Line to position the cursor on after applying <see cref="Changes"/>. | ||
/// </summary> | ||
[JsonConverter(typeof(ZeroBasedIndexConverter))] | ||
public int? Line { get; set; } | ||
/// <summary> | ||
/// Column to position the cursor on after applying <see cref="Changes"/>. | ||
/// </summary> | ||
[JsonConverter(typeof(ZeroBasedIndexConverter))] | ||
public int? Column { get; set; } | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
114 changes: 114 additions & 0 deletions
114
src/OmniSharp.Roslyn.CSharp/Services/Completion/CompletionListBuilder_Async.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,114 @@ | ||
#nullable enable | ||
|
||
using Microsoft.CodeAnalysis; | ||
using Microsoft.CodeAnalysis.Completion; | ||
using Microsoft.CodeAnalysis.Text; | ||
using OmniSharp.Models; | ||
using OmniSharp.Models.v1.Completion; | ||
using OmniSharp.Roslyn.CSharp.Services.Intellisense; | ||
using System.Collections.Generic; | ||
using System.Collections.Immutable; | ||
using System.Diagnostics; | ||
using System.Threading.Tasks; | ||
using CompletionItem = OmniSharp.Models.v1.Completion.CompletionItem; | ||
using CSharpCompletionList = Microsoft.CodeAnalysis.Completion.CompletionList; | ||
using CSharpCompletionService = Microsoft.CodeAnalysis.Completion.CompletionService; | ||
|
||
namespace OmniSharp.Roslyn.CSharp.Services.Completion | ||
{ | ||
internal static partial class CompletionListBuilder | ||
{ | ||
internal static async Task<(IReadOnlyList<CompletionItem>, bool)> BuildCompletionItemsAsync( | ||
Document document, | ||
SourceText sourceText, | ||
long cacheId, | ||
int position, | ||
CSharpCompletionService completionService, | ||
CSharpCompletionList completions, | ||
TextSpan typedSpan, | ||
bool expectingImportedItems, bool isSuggestionMode) | ||
{ | ||
var completionsBuilder = new List<CompletionItem>(completions.Items.Length); | ||
var seenUnimportedCompletions = false; | ||
var commitCharacterRuleCache = new Dictionary<ImmutableArray<CharacterSetModificationRule>, IReadOnlyList<char>>(); | ||
var commitCharacterRuleBuilder = new HashSet<char>(); | ||
var isOverrideOrPartialCompletion = completions.Items.Length > 0 | ||
&& completions.Items[0].GetProviderName() is CompletionItemExtensions.OverrideCompletionProvider or CompletionItemExtensions.PartialMethodCompletionProvider; | ||
|
||
for (int i = 0; i < completions.Items.Length; i++) | ||
{ | ||
var completion = completions.Items[i]; | ||
string labelText = completion.DisplayTextPrefix + completion.DisplayText + completion.DisplayTextSuffix; | ||
string? insertText; | ||
string? filterText = null; | ||
List<LinePositionSpanTextChange>? additionalTextEdits = null; | ||
InsertTextFormat insertTextFormat = InsertTextFormat.PlainText; | ||
TextSpan changeSpan; | ||
string? sortText; | ||
bool hasAfterInsertStep = false; | ||
if (completion.IsComplexTextEdit) | ||
{ | ||
// The completion is somehow expensive. Currently, this one of two categories: import completion, or override/partial | ||
// completion. | ||
Debug.Assert(completion.GetProviderName() is CompletionItemExtensions.OverrideCompletionProvider or CompletionItemExtensions.PartialMethodCompletionProvider | ||
or CompletionItemExtensions.TypeImportCompletionProvider or CompletionItemExtensions.ExtensionMethodImportCompletionProvider); | ||
|
||
changeSpan = typedSpan; | ||
|
||
if (isOverrideOrPartialCompletion) | ||
{ | ||
// For override and partial completion, we don't want to use the DisplayText as the insert text because they contain | ||
// characters that will affect our ability to asynchronously resolve the change later. | ||
insertText = completion.FilterText; | ||
sortText = GetSortText(completion, labelText, expectingImportedItems); | ||
hasAfterInsertStep = true; | ||
} | ||
else | ||
{ | ||
insertText = completion.DisplayText; | ||
sortText = '1' + completion.SortText; | ||
seenUnimportedCompletions = true; | ||
} | ||
} | ||
else | ||
{ | ||
// For non-complex completions, just await the text edit. It's cheap enough that it doesn't impact our ability | ||
// to pop completions quickly | ||
|
||
// If the completion item is the misc project name, skip it. | ||
if (completion.DisplayText == Configuration.OmniSharpMiscProjectName) continue; | ||
|
||
GetCompletionInfo( | ||
sourceText, | ||
position, | ||
completion, | ||
await completionService.GetChangeAsync(document, completion), | ||
typedSpan, | ||
labelText, | ||
expectingImportedItems, | ||
out insertText, out filterText, out sortText, out insertTextFormat, out changeSpan, out additionalTextEdits); | ||
} | ||
|
||
var commitCharacters = BuildCommitCharacters(completion.Rules.CommitCharacterRules, isSuggestionMode, commitCharacterRuleCache, commitCharacterRuleBuilder); | ||
|
||
completionsBuilder.Add(new CompletionItem | ||
{ | ||
Label = labelText, | ||
TextEdit = GetChangeForTextAndSpan(insertText!, changeSpan, sourceText), | ||
InsertTextFormat = insertTextFormat, | ||
AdditionalTextEdits = additionalTextEdits, | ||
SortText = sortText, | ||
FilterText = filterText, | ||
Kind = GetCompletionItemKind(completion.Tags), | ||
Detail = completion.InlineDescription, | ||
Data = (cacheId, i), | ||
Preselect = completion.Rules.SelectionBehavior == CompletionItemSelectionBehavior.HardSelection, | ||
CommitCharacters = commitCharacters, | ||
HasAfterInsertStep = hasAfterInsertStep, | ||
}); | ||
} | ||
|
||
return (completionsBuilder, seenUnimportedCompletions); | ||
} | ||
} | ||
} |
Oops, something went wrong.