Skip to content

Commit

Permalink
(OmniSharp#2209) implemented /v2/gotodefinition for Cake
Browse files Browse the repository at this point in the history
  • Loading branch information
nils-a committed Aug 22, 2021
1 parent 0cac9bd commit 918b1f7
Show file tree
Hide file tree
Showing 6 changed files with 471 additions and 86 deletions.
Original file line number Diff line number Diff line change
@@ -1,11 +1,7 @@
using System;
using System.Composition;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.FindSymbols;
using Microsoft.CodeAnalysis.Text;
using OmniSharp.Extensions;
using OmniSharp.Mef;
using OmniSharp.Models.GotoDefinition;
Expand All @@ -18,8 +14,6 @@ namespace OmniSharp.Cake.Services.RequestHandlers.Navigation
[OmniSharpHandler(OmniSharpEndpoints.GotoDefinition, Constants.LanguageNames.Cake), Shared]
public class GotoDefinitionHandler : CakeRequestHandler<GotoDefinitionRequest, GotoDefinitionResponse>
{
private const int MethodLineOffset = 3;
private const int PropertyLineOffset = 7;
private readonly MetadataExternalSourceService _metadataExternalSourceService;

[ImportingConstructor]
Expand Down Expand Up @@ -48,90 +42,31 @@ protected override async Task<GotoDefinitionResponse> TranslateResponse(GotoDefi
return new GotoDefinitionResponse();
}

return await GetAliasFromMetadataAsync(new GotoDefinitionRequest
{
Line = response.Line,
Column = response.Column,
FileName = request.FileName,
Timeout = request.Timeout,
WantMetadata = true
});
}

private async Task<GotoDefinitionResponse> GetAliasFromMetadataAsync(GotoDefinitionRequest request)
{
var document = Workspace.GetDocument(request.FileName);
var response = new GotoDefinitionResponse();
var lineIndex = request.Line + MethodLineOffset;
var column = 0;
var alias = (await GotoDefinitionHandlerHelper.GetAliasFromMetadataAsync(
Workspace,
request.FileName,
response.Line,
request.Timeout,
_metadataExternalSourceService
)).FirstOrDefault();

if (document == null)
if (alias == null)
{
return response;
return new GotoDefinitionResponse();
}

var semanticModel = await document.GetSemanticModelAsync();
var sourceText = await document.GetTextAsync();
var sourceLine = sourceText.Lines[lineIndex].ToString();
if (sourceLine.Contains("(Context"))
{
column = sourceLine.IndexOf("(Context", StringComparison.Ordinal);
}
else
return new GotoDefinitionResponse
{
lineIndex = request.Line + PropertyLineOffset;
sourceLine = sourceText.Lines[lineIndex].ToString();
if (sourceLine.Contains("(Context"))
FileName = alias.MetadataDocument.FilePath ?? alias.MetadataDocument.Name,
Line = alias.LineSpan.StartLinePosition.Line,
Column = alias.LineSpan.StartLinePosition.Character,
MetadataSource = new MetadataSource
{
column = sourceLine.IndexOf("(Context", StringComparison.Ordinal);
AssemblyName = alias.Symbol.ContainingAssembly.Name,
ProjectName = alias.Document.Project.Name,
TypeName = alias.Symbol.GetSymbolName()
}
else
{
return response;
}
}
var position = sourceText.Lines.GetPosition(new LinePosition(lineIndex, column));
var symbol = await SymbolFinder.FindSymbolAtPositionAsync(semanticModel, position, Workspace);

if (symbol == null || symbol is INamespaceSymbol)
{
return response;
}
if (symbol is IMethodSymbol method)
{
symbol = method.PartialImplementationPart ?? symbol;
}

var location = symbol.Locations.First();

if (!location.IsInMetadata)
{
return response;
}
var cancellationSource = new CancellationTokenSource(TimeSpan.FromMilliseconds(request.Timeout));
var (metadataDocument, _) = await _metadataExternalSourceService.GetAndAddExternalSymbolDocument(document.Project, symbol, cancellationSource.Token);
if (metadataDocument == null)
{
return response;
}

cancellationSource = new CancellationTokenSource(TimeSpan.FromMilliseconds(request.Timeout));
var metadataLocation = await _metadataExternalSourceService.GetExternalSymbolLocation(symbol, metadataDocument, cancellationSource.Token);
var lineSpan = metadataLocation.GetMappedLineSpan();

response = new GotoDefinitionResponse
{
Line = lineSpan.StartLinePosition.Line,
Column = lineSpan.StartLinePosition.Character,
MetadataSource = new MetadataSource()
{
AssemblyName = symbol.ContainingAssembly.Name,
ProjectName = document.Project.Name,
TypeName = symbol.GetSymbolName()
},
};

return response;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.FindSymbols;
using Microsoft.CodeAnalysis.Text;
using OmniSharp.Roslyn;

namespace OmniSharp.Cake.Services.RequestHandlers.Navigation
{
public static class GotoDefinitionHandlerHelper
{
private const int MethodLineOffset = 3;
private const int PropertyLineOffset = 7;

internal static async Task<IEnumerable<Alias>> GetAliasFromMetadataAsync(
OmniSharpWorkspace workspace,
string fileName,
int line,
int timeout,
MetadataExternalSourceService metadataExternalSourceService)
{
var document = workspace.GetDocument(fileName);
var lineIndex = line + MethodLineOffset;
int column;

if (document == null)
{
return Enumerable.Empty<Alias>();
}

var semanticModel = await document.GetSemanticModelAsync();
var sourceText = await document.GetTextAsync();
var sourceLine = sourceText.Lines[lineIndex].ToString();
if (sourceLine.Contains("(Context"))
{
column = sourceLine.IndexOf("(Context", StringComparison.Ordinal);
}
else
{
lineIndex = line + PropertyLineOffset;
sourceLine = sourceText.Lines[lineIndex].ToString();
if (sourceLine.Contains("(Context"))
{
column = sourceLine.IndexOf("(Context", StringComparison.Ordinal);
}
else
{
return Enumerable.Empty<Alias>();
}
}

if (column > 0 && sourceLine[column - 1] == '>')
{
column = sourceLine.LastIndexOf("<", column, StringComparison.Ordinal);
}

var position = sourceText.Lines.GetPosition(new LinePosition(lineIndex, column));
var symbol = await SymbolFinder.FindSymbolAtPositionAsync(semanticModel, position, workspace);

if (symbol == null || symbol is INamespaceSymbol)
{
return Enumerable.Empty<Alias>();
}
if (symbol is IMethodSymbol method)
{
symbol = method.PartialImplementationPart ?? symbol;
}

var result = new List<Alias>();
foreach (var location in symbol.Locations)
{
if (!location.IsInMetadata)
{
continue;
}

var cancellationSource = new CancellationTokenSource(TimeSpan.FromMilliseconds(timeout));
var (metadataDocument, _) = await metadataExternalSourceService.GetAndAddExternalSymbolDocument(document.Project, symbol, cancellationSource.Token);
if (metadataDocument == null)
{
continue;
}

cancellationSource = new CancellationTokenSource(TimeSpan.FromMilliseconds(timeout));
var metadataLocation = await metadataExternalSourceService.GetExternalSymbolLocation(symbol, metadataDocument, cancellationSource.Token);
var lineSpan = metadataLocation.GetMappedLineSpan();

result.Add(new Alias
{
Document = document,
MetadataDocument = metadataDocument,
Symbol = symbol,
Location = location,
LineSpan = lineSpan
});
}

return result;
}

internal class Alias
{
public Document Document { get; set; }
public ISymbol Symbol { get; set; }
public Location Location { get; set; }
public FileLinePositionSpan LineSpan { get; set; }
public Document MetadataDocument { get; set; }
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
using System;
using System.Collections.Generic;
using System.Composition;
using System.Linq;
using System.Threading.Tasks;
using OmniSharp.Extensions;
using OmniSharp.Mef;
using OmniSharp.Models.V2.GotoDefinition;
using OmniSharp.Models.Metadata;
using OmniSharp.Models.v1.SourceGeneratedFile;
using OmniSharp.Models.V2;
using OmniSharp.Roslyn;
using OmniSharp.Utilities;
using Location = OmniSharp.Models.V2.Location;

namespace OmniSharp.Cake.Services.RequestHandlers.Navigation
{
[OmniSharpHandler(OmniSharpEndpoints.V2.GotoDefinition, Constants.LanguageNames.Cake), Shared]
public class GotoDefinitionV2Handler : CakeRequestHandler<GotoDefinitionRequest, GotoDefinitionResponse>
{
private readonly MetadataExternalSourceService _metadataExternalSourceService;

[ImportingConstructor]
public GotoDefinitionV2Handler(
OmniSharpWorkspace workspace,
MetadataExternalSourceService metadataExternalSourceService)
: base(workspace)
{
_metadataExternalSourceService = metadataExternalSourceService ?? throw new ArgumentNullException(nameof(metadataExternalSourceService));
}

protected override async Task<GotoDefinitionResponse> TranslateResponse(GotoDefinitionResponse response, GotoDefinitionRequest request)
{
var definitions = new List<Definition>();
foreach (var definition in response.Definitions ?? Enumerable.Empty<Definition>())
{
var file = definition.Location.FileName;

if (string.IsNullOrEmpty(file) || !file.Equals(Constants.Paths.Generated))
{
if (PlatformHelper.IsWindows && !string.IsNullOrEmpty(file))
{
file = file.Replace('/', '\\');
}

definitions.Add(new Definition
{
MetadataSource = definition.MetadataSource,
SourceGeneratedFileInfo = definition.SourceGeneratedFileInfo,
Location = new Location
{
FileName = file,
Range = definition.Location.Range
}
});

continue;
}

if (!request.WantMetadata)
{
continue;
}

var aliasLocations = await GotoDefinitionHandlerHelper.GetAliasFromMetadataAsync(
Workspace,
request.FileName,
definition.Location.Range.End.Line,
request.Timeout,
_metadataExternalSourceService
);

definitions.AddRange(
aliasLocations.Select(loc =>
new Definition
{
Location = new Location
{
FileName = loc.MetadataDocument.FilePath ?? loc.MetadataDocument.Name,
Range = new Range
{
Start = new Point
{
Column = loc.LineSpan.StartLinePosition.Character,
Line = loc.LineSpan.StartLinePosition.Line
},
End = new Point
{
Column = loc.LineSpan.EndLinePosition.Character,
Line = loc.LineSpan.EndLinePosition.Line
},
}
},
MetadataSource = new MetadataSource
{
AssemblyName = loc.Symbol.ContainingAssembly.Name,
ProjectName = loc.Document.Project.Name,
TypeName = loc.Symbol.GetSymbolName()
},
SourceGeneratedFileInfo = new SourceGeneratedFileInfo
{
DocumentGuid = loc.Document.Id.Id,
ProjectGuid = loc.Document.Id.ProjectId.Id
}
})
.ToList());
}

return new GotoDefinitionResponse
{
Definitions = definitions
};
}
}
}
7 changes: 6 additions & 1 deletion test-assets/test-projects/CakeProject/foo.cake
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
var HelloText = "Hello World!";

public class Foo
public partial class Foo
{
public static Foo Create()
{
Expand All @@ -11,4 +11,9 @@ public class Foo
{
return;
}
}

public partial class Foo
{

}
Loading

0 comments on commit 918b1f7

Please sign in to comment.