-
Notifications
You must be signed in to change notification settings - Fork 420
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #2456 from bjorkstromm/feature/cake-v2-highlight
Adds V2 Highlight support to Cake
- Loading branch information
Showing
4 changed files
with
261 additions
and
0 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
41 changes: 41 additions & 0 deletions
41
src/OmniSharp.Cake/Services/RequestHandlers/SemanticHighlight/SemanticHighlightHandler.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,41 @@ | ||
using System.Composition; | ||
using System.Threading.Tasks; | ||
using OmniSharp.Cake.Extensions; | ||
using OmniSharp.Cake.Utilities; | ||
using OmniSharp.Mef; | ||
using OmniSharp.Models.SemanticHighlight; | ||
|
||
namespace OmniSharp.Cake.Services.RequestHandlers.SemanticHighlight; | ||
|
||
[OmniSharpHandler(OmniSharpEndpoints.V2.Highlight, Constants.LanguageNames.Cake), Shared] | ||
public class SemanticHighlightHandler : CakeRequestHandler<SemanticHighlightRequest, SemanticHighlightResponse> | ||
{ | ||
[ImportingConstructor] | ||
public SemanticHighlightHandler(OmniSharpWorkspace workspace) : base(workspace) | ||
{ | ||
} | ||
|
||
protected override async Task<SemanticHighlightRequest> TranslateRequestAsync(SemanticHighlightRequest request) | ||
{ | ||
if (request.Range is not null) | ||
{ | ||
var startLine = await LineIndexHelper.TranslateToGenerated(request.FileName, request.Range.Start.Line, Workspace); | ||
var endLine = request.Range.Start.Line != request.Range.End.Line | ||
? await LineIndexHelper.TranslateToGenerated(request.FileName, request.Range.End.Line, Workspace) | ||
: startLine; | ||
|
||
request.Range = request.Range with | ||
{ | ||
Start = request.Range.Start with { Line = startLine }, | ||
End = request.Range.End with { Line = endLine } | ||
}; | ||
} | ||
|
||
return await base.TranslateRequestAsync(request); | ||
} | ||
|
||
protected override Task<SemanticHighlightResponse> TranslateResponse(SemanticHighlightResponse response, SemanticHighlightRequest request) | ||
{ | ||
return response.TranslateAsync(Workspace, request); | ||
} | ||
} |
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,183 @@ | ||
using System; | ||
using System.IO; | ||
using System.Threading.Tasks; | ||
using Microsoft.CodeAnalysis.Text; | ||
using OmniSharp.Cake.Services.RequestHandlers.SemanticHighlight; | ||
using OmniSharp.Models.SemanticHighlight; | ||
using OmniSharp.Models.UpdateBuffer; | ||
using OmniSharp.Models.V2; | ||
using TestUtility; | ||
using Xunit; | ||
using Xunit.Abstractions; | ||
using Range = OmniSharp.Models.V2.Range; | ||
|
||
namespace OmniSharp.Cake.Tests | ||
{ | ||
public class SemanticHighlightFacts : CakeSingleRequestHandlerTestFixture<SemanticHighlightHandler> | ||
{ | ||
public SemanticHighlightFacts(ITestOutputHelper testOutput) : base(testOutput) | ||
{ | ||
} | ||
|
||
protected override string EndpointName => OmniSharpEndpoints.V2.Highlight; | ||
|
||
[Fact] | ||
public async Task SemanticHighlightSingleLine() | ||
{ | ||
const string source = @" | ||
class C1 | ||
{ | ||
class C2 { int n = true; } | ||
} | ||
"; | ||
|
||
var line = 3; | ||
var highlights = await GetSemanticHighlightsForLineAsync(source, line, versionedText: null); | ||
|
||
AssertSyntax(highlights, source, line, | ||
Keyword("class"), | ||
ClassName("C2"), | ||
Punctuation("{"), | ||
Keyword("int"), | ||
Field("n"), | ||
Operator("="), | ||
Keyword("true"), | ||
Punctuation(";"), | ||
Punctuation("}")); | ||
} | ||
|
||
[Fact] | ||
public async Task SemanticHighlightEntireFile() | ||
{ | ||
const string source = @" | ||
class C1 | ||
{ | ||
class C2 { int n = true; } | ||
} | ||
"; | ||
|
||
var highlights = await GetSemanticHighlightsForFileAsync(source); | ||
|
||
AssertSyntax(highlights, source, 0, | ||
Keyword("class"), | ||
ClassName("C1"), | ||
Punctuation("{"), | ||
Keyword("class"), | ||
ClassName("C2"), | ||
Punctuation("{"), | ||
Keyword("int"), | ||
Field("n"), | ||
Operator("="), | ||
Keyword("true"), | ||
Punctuation(";"), | ||
Punctuation("}"), | ||
Punctuation("}") | ||
); | ||
} | ||
|
||
private Task<SemanticHighlightSpan[]> GetSemanticHighlightsForFileAsync(string source) | ||
{ | ||
return GetSemanticHighlightsAsync(source, range: null, versionedText: null); | ||
} | ||
|
||
private Task<SemanticHighlightSpan[]> GetSemanticHighlightsForLineAsync(string source, int line, string versionedText) | ||
{ | ||
var range = new Range() | ||
{ | ||
Start = new Point() { Column = 0, Line = line }, | ||
End = new Point() { Column = 0, Line = line + 1 } | ||
}; | ||
|
||
return GetSemanticHighlightsAsync(source, range, versionedText); | ||
} | ||
|
||
private async Task<SemanticHighlightSpan[]> GetSemanticHighlightsAsync(string source, Range range, string versionedText) | ||
{ | ||
using (var testProject = await TestAssets.Instance.GetTestProjectAsync("CakeProject", shadowCopy : false)) | ||
using (var host = CreateOmniSharpHost(testProject.Directory)) | ||
{ | ||
var testFile = new TestFile(Path.Combine(testProject.Directory, "build.cake"), source); | ||
|
||
var request = new SemanticHighlightRequest | ||
{ | ||
FileName = testFile.FileName, | ||
Range = range, | ||
VersionedText = versionedText, | ||
}; | ||
|
||
var updateBufferRequest = new UpdateBufferRequest | ||
{ | ||
Buffer = source, | ||
FileName = request.FileName, | ||
FromDisk = false, | ||
}; | ||
|
||
await GetUpdateBufferHandler(host).Handle(updateBufferRequest); | ||
|
||
var requestHandler = GetRequestHandler(host); | ||
|
||
var response = await requestHandler.Handle(request); | ||
|
||
return response.Spans; | ||
} | ||
} | ||
|
||
private static void AssertSyntax(SemanticHighlightSpan[] highlights, string code, int startLine, params (SemanticHighlightClassification kind, string text, SemanticHighlightModifier[] modifiers)[] expectedTokens) | ||
{ | ||
var lineNo = startLine; | ||
var lastIndex = 0; | ||
var lines = SourceText.From(code).Lines; | ||
|
||
for (var i = 0; i < highlights.Length; i++) | ||
{ | ||
var (type, text, modifiers) = expectedTokens[i]; | ||
var highlight = highlights[i]; | ||
|
||
string line; | ||
int start, end; | ||
do | ||
{ | ||
line = lines[lineNo].ToString(); | ||
start = line.IndexOf(text, lastIndex); | ||
if (start == -1) | ||
{ | ||
if (++lineNo >= lines.Count) | ||
{ | ||
throw new Exception($"Could not find token {text} in the code"); | ||
} | ||
|
||
lastIndex = 0; | ||
} | ||
} | ||
while (start == -1); | ||
|
||
end = start + text.Length; | ||
lastIndex = end; | ||
|
||
Assert.Equal(type, highlight.Type); | ||
Assert.Equal(modifiers, highlight.Modifiers); | ||
Assert.Equal(lineNo, highlight.StartLine); | ||
Assert.Equal(lineNo, highlight.EndLine); | ||
Assert.Equal(start, highlight.StartColumn); | ||
Assert.Equal(end, highlight.EndColumn); | ||
} | ||
|
||
Assert.Equal(expectedTokens.Length, highlights.Length); | ||
} | ||
|
||
private static (SemanticHighlightClassification type, string text, SemanticHighlightModifier[] modifiers) Method(string text, params SemanticHighlightModifier[] modifiers) => (SemanticHighlightClassification.MethodName, text, modifiers); | ||
private static (SemanticHighlightClassification type, string text, SemanticHighlightModifier[] modifiers) Local(string text, params SemanticHighlightModifier[] modifiers) => (SemanticHighlightClassification.LocalName, text, modifiers); | ||
private static (SemanticHighlightClassification type, string text, SemanticHighlightModifier[] modifiers) ClassName(string text, params SemanticHighlightModifier[] modifiers) => (SemanticHighlightClassification.ClassName, text, modifiers); | ||
private static (SemanticHighlightClassification type, string text, SemanticHighlightModifier[] modifiers) StructName(string text, params SemanticHighlightModifier[] modifiers) => (SemanticHighlightClassification.StructName, text, modifiers); | ||
private static (SemanticHighlightClassification type, string text, SemanticHighlightModifier[] modifiers) Field(string text, params SemanticHighlightModifier[] modifiers) => (SemanticHighlightClassification.FieldName, text, modifiers); | ||
private static (SemanticHighlightClassification type, string text, SemanticHighlightModifier[] modifiers) Identifier(string text, params SemanticHighlightModifier[] modifiers) => (SemanticHighlightClassification.Identifier, text, modifiers); | ||
private static (SemanticHighlightClassification type, string text, SemanticHighlightModifier[] modifiers) Parameter(string text, params SemanticHighlightModifier[] modifiers) => (SemanticHighlightClassification.ParameterName, text, modifiers); | ||
private static (SemanticHighlightClassification type, string text, SemanticHighlightModifier[] modifiers) NamespaceName(string text, params SemanticHighlightModifier[] modifiers) => (SemanticHighlightClassification.NamespaceName, text, modifiers); | ||
private static (SemanticHighlightClassification type, string text, SemanticHighlightModifier[] modifiers) Keyword(string text, params SemanticHighlightModifier[] modifiers) => (SemanticHighlightClassification.Keyword, text, modifiers); | ||
private static (SemanticHighlightClassification type, string text, SemanticHighlightModifier[] modifiers) ControlKeyword(string text, params SemanticHighlightModifier[] modifiers) => (SemanticHighlightClassification.ControlKeyword, text, modifiers); | ||
private static (SemanticHighlightClassification type, string text, SemanticHighlightModifier[] modifiers) Number(string text, params SemanticHighlightModifier[] modifiers) => (SemanticHighlightClassification.NumericLiteral, text, modifiers); | ||
private static (SemanticHighlightClassification type, string text, SemanticHighlightModifier[] modifiers) Operator(string text, params SemanticHighlightModifier[] modifiers) => (SemanticHighlightClassification.Operator, text, modifiers); | ||
private static (SemanticHighlightClassification type, string text, SemanticHighlightModifier[] modifiers) Punctuation(string text, params SemanticHighlightModifier[] modifiers) => (SemanticHighlightClassification.Punctuation, text, modifiers); | ||
private static (SemanticHighlightClassification type, string text, SemanticHighlightModifier[] modifiers) String(string text, params SemanticHighlightModifier[] modifiers) => (SemanticHighlightClassification.StringLiteral, text, modifiers); | ||
} | ||
} |