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 @@ -47,13 +47,30 @@ public TextDocumentIdentifier GetTextDocumentIdentifier(WrapWithTagParams reques
return null;
}

var sourceText = await documentContext.GetSourceTextAsync(cancellationToken).ConfigureAwait(false);
var sourceText = codeDocument.Source.Text;
if (request.Range?.Start is not { } start ||
!sourceText.TryGetAbsoluteIndex(start, out var hostDocumentIndex))
{
return null;
}

// First thing we do is make sure we start at a non-whitespace character. This is important because in some
// situations the whitespace can be technically C#, but move one character to the right and it's HTML. eg
//
// @if (true) {
// | <p></p>
// }
//
// Limiting this to only whitespace on the same line, as it's not clear what user expectation would be otherwise.
var requestSpan = sourceText.GetTextSpan(request.Range);
if (sourceText.TryGetFirstNonWhitespaceOffset(requestSpan, out var offset, out var newLineCount) &&
newLineCount == 0)
{
request.Range.Start.Character += offset;
requestSpan = sourceText.GetTextSpan(request.Range);
hostDocumentIndex += offset;
}

// Since we're at the start of the selection, lets prefer the language to the right of the cursor if possible.
// That way with the following situation:
//
Expand Down Expand Up @@ -89,7 +106,6 @@ public TextDocumentIdentifier GetTextDocumentIdentifier(WrapWithTagParams reques
// <p>[|@currentCount|]</p>

var tree = await documentContext.GetSyntaxTreeAsync(cancellationToken).ConfigureAwait(false);
var requestSpan = sourceText.GetTextSpan(request.Range);
var node = tree.Root.FindNode(requestSpan, includeWhitespace: false, getInnermostNodeForTie: true);
if (node?.FirstAncestorOrSelf<CSharpImplicitExpressionSyntax>() is { Parent: CSharpCodeBlockSyntax codeBlock } &&
(requestSpan == codeBlock.FullSpan || requestSpan.Length == 0))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,114 @@ public async Task Handle_RazorBlockStart_ReturnsResult()
Mock.Get(clientConnection).Verify();
}

[Fact]
public async Task Handle_HtmlInCSharp()
{
// Arrange
var input = new TestCode("""
@if (true)
{
[|<p></p>|]
}
""");
var codeDocument = CreateCodeDocument(input.Text);
var uri = new Uri("file://path/test.razor");
var documentContext = CreateDocumentContext(uri, codeDocument);
var response = new WrapWithTagResponse();

var clientConnection = TestMocks.CreateClientConnection(builder =>
{
builder.SetupSendRequest<WrapWithTagParams, WrapWithTagResponse>(LanguageServerConstants.RazorWrapWithTagEndpoint, response: new(), verifiable: true);
});

var endpoint = new WrapWithTagEndpoint(clientConnection, LoggerFactory);

var range = codeDocument.Source.Text.GetRange(input.Span);
var wrapWithDivParams = new WrapWithTagParams(new TextDocumentIdentifier { Uri = uri })
{
Range = range
};
var requestContext = CreateRazorRequestContext(documentContext);

// Act
var result = await endpoint.HandleRequestAsync(wrapWithDivParams, requestContext, DisposalToken);

// Assert
Assert.NotNull(result);
Mock.Get(clientConnection).Verify();
}

[Fact]
public async Task Handle_HtmlInCSharp_WithWhitespace()
{
// Arrange
var input = new TestCode("""
@if (true)
{
[| <p></p>|]
}
""");
var codeDocument = CreateCodeDocument(input.Text);
var uri = new Uri("file://path/test.razor");
var documentContext = CreateDocumentContext(uri, codeDocument);
var response = new WrapWithTagResponse();

var clientConnection = TestMocks.CreateClientConnection(builder =>
{
builder.SetupSendRequest<WrapWithTagParams, WrapWithTagResponse>(LanguageServerConstants.RazorWrapWithTagEndpoint, response: new(), verifiable: true);
});

var endpoint = new WrapWithTagEndpoint(clientConnection, LoggerFactory);

var range = codeDocument.Source.Text.GetRange(input.Span);
var wrapWithDivParams = new WrapWithTagParams(new TextDocumentIdentifier { Uri = uri })
{
Range = range
};
var requestContext = CreateRazorRequestContext(documentContext);

// Act
var result = await endpoint.HandleRequestAsync(wrapWithDivParams, requestContext, DisposalToken);

// Assert
Assert.NotNull(result);
Mock.Get(clientConnection).Verify();
}

[Fact]
public async Task Handle_HtmlInCSharp_WithNewline()
{
// Arrange
var input = new TestCode("""
@if (true)
{[|
<p></p>|]
}
""");
var codeDocument = CreateCodeDocument(input.Text);
var uri = new Uri("file://path/test.razor");
var documentContext = CreateDocumentContext(uri, codeDocument);
var response = new WrapWithTagResponse();

var clientConnection = TestMocks.CreateClientConnection(builder => { });

var endpoint = new WrapWithTagEndpoint(clientConnection, LoggerFactory);

var range = codeDocument.Source.Text.GetRange(input.Span);
var wrapWithDivParams = new WrapWithTagParams(new TextDocumentIdentifier { Uri = uri })
{
Range = range
};
var requestContext = CreateRazorRequestContext(documentContext);

// Act
var result = await endpoint.HandleRequestAsync(wrapWithDivParams, requestContext, DisposalToken);

// Assert
Assert.Null(result);
Mock.Get(clientConnection).Verify();
}

[Fact]
public async Task Handle_CSharp_PartOfImplicitStatement_ReturnsNull()
{
Expand Down