Skip to content

Commit 9a8b09f

Browse files
committed
Merge remote-tracking branch 'origin/master' into OptionsExternalAccess
2 parents 43ca318 + 6aace68 commit 9a8b09f

File tree

14 files changed

+1093
-14
lines changed

14 files changed

+1093
-14
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
using OmniSharp.Mef;
2+
3+
namespace OmniSharp.Models.GotoTypeDefinition
4+
{
5+
[OmniSharpEndpoint(OmniSharpEndpoints.GotoTypeDefinition, typeof(GotoTypeDefinitionRequest), typeof(GotoTypeDefinitionResponse))]
6+
public class GotoTypeDefinitionRequest : Request
7+
{
8+
public int Timeout { get; init; } = 10000;
9+
public bool WantMetadata { get; init; }
10+
}
11+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
#nullable enable
2+
3+
using OmniSharp.Models.Metadata;
4+
using OmniSharp.Models.v1.SourceGeneratedFile;
5+
using System.Collections.Generic;
6+
7+
namespace OmniSharp.Models.GotoTypeDefinition
8+
{
9+
public record GotoTypeDefinitionResponse
10+
{
11+
public List<TypeDefinition>? Definitions { get; init; }
12+
}
13+
14+
public record TypeDefinition
15+
{
16+
public V2.Location Location { get; init; } = null!;
17+
public MetadataSource? MetadataSource { get; init; }
18+
public SourceGeneratedFileInfo? SourceGeneratedFileInfo { get; init; }
19+
}
20+
}

src/OmniSharp.Abstractions/OmniSharpEndpoints.cs

+1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ namespace OmniSharp
33
public static class OmniSharpEndpoints
44
{
55
public const string GotoDefinition = "/gotodefinition";
6+
public const string GotoTypeDefinition = "/gototypedefinition";
67
public const string FindSymbols = "/findsymbols";
78
public const string UpdateBuffer = "/updatebuffer";
89
public const string ChangeBuffer = "/changebuffer";

src/OmniSharp.Cake/OmniSharp.Cake.csproj

+1
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
<ItemGroup>
99
<ProjectReference Include="..\OmniSharp.Abstractions\OmniSharp.Abstractions.csproj" />
10+
<ProjectReference Include="..\OmniSharp.Roslyn.CSharp\OmniSharp.Roslyn.CSharp.csproj" />
1011
<ProjectReference Include="..\OmniSharp.Roslyn\OmniSharp.Roslyn.csproj" />
1112
<ProjectReference Include="..\OmniSharp.Shared\OmniSharp.Shared.csproj" />
1213
</ItemGroup>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Composition;
4+
using System.Linq;
5+
using System.Threading.Tasks;
6+
using OmniSharp.Extensions;
7+
using OmniSharp.Mef;
8+
using OmniSharp.Models.GotoTypeDefinition;
9+
using OmniSharp.Models.Metadata;
10+
using OmniSharp.Models.v1.SourceGeneratedFile;
11+
using OmniSharp.Models.V2;
12+
using OmniSharp.Roslyn;
13+
using OmniSharp.Utilities;
14+
using Location = OmniSharp.Models.V2.Location;
15+
using Range = OmniSharp.Models.V2.Range;
16+
using OmniSharp.Roslyn.CSharp.Services;
17+
using OmniSharp.Options;
18+
19+
namespace OmniSharp.Cake.Services.RequestHandlers.Navigation
20+
{
21+
[OmniSharpHandler(OmniSharpEndpoints.GotoTypeDefinition, Constants.LanguageNames.Cake), Shared]
22+
public class GotoTypeDefinitionHandler : CakeRequestHandler<GotoTypeDefinitionRequest, GotoTypeDefinitionResponse>
23+
{
24+
private readonly IExternalSourceService _externalSourceService;
25+
26+
[ImportingConstructor]
27+
public GotoTypeDefinitionHandler(
28+
OmniSharpWorkspace workspace,
29+
ExternalSourceServiceFactory externalSourceServiceFactory,
30+
OmniSharpOptions omniSharpOptions)
31+
: base(workspace)
32+
{
33+
_externalSourceService = externalSourceServiceFactory?.Create(omniSharpOptions) ?? throw new ArgumentNullException(nameof(externalSourceServiceFactory));
34+
}
35+
36+
protected override async Task<GotoTypeDefinitionResponse> TranslateResponse(GotoTypeDefinitionResponse response, GotoTypeDefinitionRequest request)
37+
{
38+
var definitions = new List<TypeDefinition>();
39+
foreach (var definition in response.Definitions ?? Enumerable.Empty<TypeDefinition>())
40+
{
41+
var file = definition.Location.FileName;
42+
43+
if (string.IsNullOrEmpty(file) || !file.Equals(Constants.Paths.Generated))
44+
{
45+
if (PlatformHelper.IsWindows && !string.IsNullOrEmpty(file))
46+
{
47+
file = file.Replace('/', '\\');
48+
}
49+
50+
definitions.Add(new TypeDefinition
51+
{
52+
MetadataSource = definition.MetadataSource,
53+
SourceGeneratedFileInfo = definition.SourceGeneratedFileInfo,
54+
Location = new Location
55+
{
56+
FileName = file,
57+
Range = definition.Location.Range
58+
}
59+
});
60+
61+
continue;
62+
}
63+
64+
if (!request.WantMetadata)
65+
{
66+
continue;
67+
}
68+
69+
var aliasLocations = await GotoTypeDefinitionHandlerHelper.GetAliasFromExternalSourceAsync(
70+
Workspace,
71+
request.FileName,
72+
definition.Location.Range.End.Line,
73+
request.Timeout,
74+
_externalSourceService
75+
);
76+
77+
definitions.AddRange(
78+
aliasLocations.Select(loc =>
79+
new TypeDefinition
80+
{
81+
Location = new Location
82+
{
83+
FileName = loc.MetadataDocument.FilePath ?? loc.MetadataDocument.Name,
84+
Range = new Range
85+
{
86+
Start = new Point
87+
{
88+
Column = loc.LineSpan.StartLinePosition.Character,
89+
Line = loc.LineSpan.StartLinePosition.Line
90+
},
91+
End = new Point
92+
{
93+
Column = loc.LineSpan.EndLinePosition.Character,
94+
Line = loc.LineSpan.EndLinePosition.Line
95+
},
96+
}
97+
},
98+
MetadataSource = new MetadataSource
99+
{
100+
AssemblyName = loc.Symbol.ContainingAssembly.Name,
101+
ProjectName = loc.Document.Project.Name,
102+
TypeName = loc.Symbol.GetSymbolName()
103+
},
104+
SourceGeneratedFileInfo = new SourceGeneratedFileInfo
105+
{
106+
DocumentGuid = loc.Document.Id.Id,
107+
ProjectGuid = loc.Document.Id.ProjectId.Id
108+
}
109+
})
110+
.ToList());
111+
}
112+
113+
return new GotoTypeDefinitionResponse
114+
{
115+
Definitions = definitions
116+
};
117+
}
118+
}
119+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using System.Threading;
5+
using System.Threading.Tasks;
6+
using Microsoft.CodeAnalysis;
7+
using Microsoft.CodeAnalysis.FindSymbols;
8+
using Microsoft.CodeAnalysis.Text;
9+
using OmniSharp.Roslyn;
10+
using OmniSharp.Roslyn.CSharp.Services;
11+
12+
namespace OmniSharp.Cake.Services.RequestHandlers.Navigation
13+
{
14+
public static class GotoTypeDefinitionHandlerHelper
15+
{
16+
private const int MethodLineOffset = 3;
17+
private const int PropertyLineOffset = 7;
18+
19+
internal static async Task<IEnumerable<Alias>> GetAliasFromExternalSourceAsync(
20+
OmniSharpWorkspace workspace,
21+
string fileName,
22+
int line,
23+
int timeout,
24+
IExternalSourceService externalSourceService)
25+
{
26+
var document = workspace.GetDocument(fileName);
27+
var lineIndex = line + MethodLineOffset;
28+
int column;
29+
30+
if (document == null)
31+
{
32+
return Enumerable.Empty<Alias>();
33+
}
34+
35+
var semanticModel = await document.GetSemanticModelAsync();
36+
var sourceText = await document.GetTextAsync();
37+
var sourceLine = sourceText.Lines[lineIndex].ToString();
38+
if (sourceLine.Contains("(Context"))
39+
{
40+
column = sourceLine.IndexOf("(Context", StringComparison.Ordinal);
41+
}
42+
else
43+
{
44+
lineIndex = line + PropertyLineOffset;
45+
sourceLine = sourceText.Lines[lineIndex].ToString();
46+
if (sourceLine.Contains("(Context"))
47+
{
48+
column = sourceLine.IndexOf("(Context", StringComparison.Ordinal);
49+
}
50+
else
51+
{
52+
return Enumerable.Empty<Alias>();
53+
}
54+
}
55+
56+
if (column > 0 && sourceLine[column - 1] == '>')
57+
{
58+
column = sourceLine.LastIndexOf("<", column, StringComparison.Ordinal);
59+
}
60+
61+
var position = sourceText.Lines.GetPosition(new LinePosition(lineIndex, column));
62+
var symbol = await SymbolFinder.FindSymbolAtPositionAsync(semanticModel, position, workspace);
63+
64+
if (symbol == null || symbol is INamespaceSymbol)
65+
{
66+
return Enumerable.Empty<Alias>();
67+
}
68+
if (symbol is IMethodSymbol method)
69+
{
70+
symbol = method.PartialImplementationPart ?? symbol;
71+
}
72+
73+
var typeSymbol = symbol switch
74+
{
75+
ILocalSymbol localSymbol => localSymbol.Type,
76+
IFieldSymbol fieldSymbol => fieldSymbol.Type,
77+
IPropertySymbol propertySymbol => propertySymbol.Type,
78+
IParameterSymbol parameterSymbol => parameterSymbol.Type,
79+
_ => null
80+
};
81+
82+
if (typeSymbol == null)
83+
return Enumerable.Empty<Alias>();
84+
85+
var result = new List<Alias>();
86+
foreach (var location in typeSymbol.Locations)
87+
{
88+
if (!location.IsInMetadata)
89+
{
90+
continue;
91+
}
92+
93+
var cancellationSource = new CancellationTokenSource(TimeSpan.FromMilliseconds(timeout));
94+
var (metadataDocument, _) = await externalSourceService.GetAndAddExternalSymbolDocument(document.Project, typeSymbol, cancellationSource.Token);
95+
if (metadataDocument == null)
96+
{
97+
continue;
98+
}
99+
100+
cancellationSource = new CancellationTokenSource(TimeSpan.FromMilliseconds(timeout));
101+
var metadataLocation = await externalSourceService.GetExternalSymbolLocation(typeSymbol, metadataDocument, cancellationSource.Token);
102+
var lineSpan = metadataLocation.GetMappedLineSpan();
103+
104+
result.Add(new Alias
105+
{
106+
Document = document,
107+
MetadataDocument = metadataDocument,
108+
Symbol = typeSymbol,
109+
Location = location,
110+
LineSpan = lineSpan
111+
});
112+
}
113+
114+
return result;
115+
}
116+
117+
internal class Alias
118+
{
119+
public Document Document { get; set; }
120+
public ISymbol Symbol { get; set; }
121+
public Location Location { get; set; }
122+
public FileLinePositionSpan LineSpan { get; set; }
123+
public Document MetadataDocument { get; set; }
124+
}
125+
}
126+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
#nullable enable
2+
3+
using Microsoft.CodeAnalysis;
4+
using Microsoft.CodeAnalysis.FindSymbols;
5+
using Microsoft.CodeAnalysis.Operations;
6+
using OmniSharp.Extensions;
7+
using OmniSharp.Models.v1.SourceGeneratedFile;
8+
using System.Diagnostics;
9+
using System.Threading;
10+
using System.Threading.Tasks;
11+
12+
namespace OmniSharp.Roslyn.CSharp.Services.Navigation
13+
{
14+
internal static class GotoTypeDefinitionHelpers
15+
{
16+
internal static async Task<ITypeSymbol?> GetTypeOfSymbol(Document document, int line, int column, CancellationToken cancellationToken)
17+
{
18+
var sourceText = await document.GetTextAsync(cancellationToken);
19+
var position = sourceText.GetPositionFromLineAndOffset(line, column);
20+
var symbol = await SymbolFinder.FindSymbolAtPositionAsync(document, position, cancellationToken);
21+
22+
return symbol switch
23+
{
24+
ILocalSymbol localSymbol => localSymbol.Type,
25+
IFieldSymbol fieldSymbol => fieldSymbol.Type,
26+
IPropertySymbol propertySymbol => propertySymbol.Type,
27+
IParameterSymbol parameterSymbol => parameterSymbol.Type,
28+
_ => null
29+
};
30+
}
31+
32+
internal static async Task<FileLinePositionSpan?> GetMetadataMappedSpan(
33+
Document document,
34+
ISymbol symbol,
35+
IExternalSourceService externalSourceService,
36+
CancellationToken cancellationToken)
37+
{
38+
var (metadataDocument, _) = await externalSourceService.GetAndAddExternalSymbolDocument(document.Project, symbol, cancellationToken);
39+
if (metadataDocument != null)
40+
{
41+
var metadataLocation = await externalSourceService.GetExternalSymbolLocation(symbol, metadataDocument, cancellationToken);
42+
return metadataLocation.GetMappedLineSpan();
43+
}
44+
45+
return null;
46+
}
47+
48+
internal static SourceGeneratedFileInfo? GetSourceGeneratedFileInfo(OmniSharpWorkspace workspace, Location location)
49+
{
50+
Debug.Assert(location.IsInSource);
51+
var document = workspace.CurrentSolution.GetDocument(location.SourceTree);
52+
if (document is not SourceGeneratedDocument)
53+
{
54+
return null;
55+
}
56+
57+
return new SourceGeneratedFileInfo
58+
{
59+
ProjectGuid = document.Project.Id.Id,
60+
DocumentGuid = document.Id.Id
61+
};
62+
}
63+
}
64+
}

0 commit comments

Comments
 (0)