From 0474a35b2ed0428c174fb7fe92f147360b485a45 Mon Sep 17 00:00:00 2001 From: David Wengier Date: Thu, 2 Jan 2025 11:49:33 +1100 Subject: [PATCH] Fix completion in an empty document --- .../IDocumentMappingServiceExtensions.cs | 12 +++++ .../CohostDocumentCompletionEndpointTest.cs | 53 +++++++++++++++++++ 2 files changed, 65 insertions(+) diff --git a/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/DocumentMapping/IDocumentMappingServiceExtensions.cs b/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/DocumentMapping/IDocumentMappingServiceExtensions.cs index ec358cd5540..7293971c6bd 100644 --- a/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/DocumentMapping/IDocumentMappingServiceExtensions.cs +++ b/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/DocumentMapping/IDocumentMappingServiceExtensions.cs @@ -2,6 +2,7 @@ // Licensed under the MIT license. See License.txt in the project root for license information. using System.Collections.Generic; +using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Threading; @@ -47,6 +48,17 @@ public static DocumentPositionInfo GetPositionInfo( int hostDocumentIndex) { var sourceText = codeDocument.Source.Text; + + if (sourceText.Length == 0) + { + Debug.Assert(hostDocumentIndex == 0); + + // Special case for empty documents, to just force Html. When there is no content, then there are no source mappings, + // so the map call below fails, and we would default to Razor. This is fine for most cases, but empty documents are a + // special case where Html provides much better results when users first start typing. + return new DocumentPositionInfo(RazorLanguageKind.Html, new Position(0, 0), hostDocumentIndex); + } + var position = sourceText.GetPosition(hostDocumentIndex); var languageKind = codeDocument.GetLanguageKind(hostDocumentIndex, rightAssociative: false); diff --git a/src/Razor/test/Microsoft.VisualStudio.LanguageServices.Razor.Test/Cohost/CohostDocumentCompletionEndpointTest.cs b/src/Razor/test/Microsoft.VisualStudio.LanguageServices.Razor.Test/Cohost/CohostDocumentCompletionEndpointTest.cs index 685ce73e866..a9413b4ce9d 100644 --- a/src/Razor/test/Microsoft.VisualStudio.LanguageServices.Razor.Test/Cohost/CohostDocumentCompletionEndpointTest.cs +++ b/src/Razor/test/Microsoft.VisualStudio.LanguageServices.Razor.Test/Cohost/CohostDocumentCompletionEndpointTest.cs @@ -313,6 +313,59 @@ The end. snippetLabels: ["snippet1", "snippet2"]); } + [Fact] + public async Task HtmlSnippetsCompletion_EmptyDocument() + { + await VerifyCompletionListAsync( + input: """ + $$ + """, + completionContext: new RoslynVSInternalCompletionContext() + { + InvokeKind = RoslynVSInternalCompletionInvokeKind.Explicit, + TriggerCharacter = null, + TriggerKind = RoslynCompletionTriggerKind.Invoked + }, + expectedItemLabels: ["snippet1", "snippet2"], + snippetLabels: ["snippet1", "snippet2"]); + } + + [Fact] + public async Task HtmlSnippetsCompletion_WhitespaceOnlyDocument1() + { + await VerifyCompletionListAsync( + input: """ + + $$ + """, + completionContext: new RoslynVSInternalCompletionContext() + { + InvokeKind = RoslynVSInternalCompletionInvokeKind.Explicit, + TriggerCharacter = null, + TriggerKind = RoslynCompletionTriggerKind.Invoked + }, + expectedItemLabels: ["snippet1", "snippet2"], + snippetLabels: ["snippet1", "snippet2"]); + } + + [Fact] + public async Task HtmlSnippetsCompletion_WhitespaceOnlyDocument2() + { + await VerifyCompletionListAsync( + input: """ + $$ + + """, + completionContext: new RoslynVSInternalCompletionContext() + { + InvokeKind = RoslynVSInternalCompletionInvokeKind.Explicit, + TriggerCharacter = null, + TriggerKind = RoslynCompletionTriggerKind.Invoked + }, + expectedItemLabels: ["snippet1", "snippet2"], + snippetLabels: ["snippet1", "snippet2"]); + } + [Fact] public async Task HtmlSnippetsCompletion_NotInStartTag() {