Forge
csharpdeeb25a2
1using Microsoft.CodeAnalysis;
2using Microsoft.CodeAnalysis.Text;
3
4namespace CascadeIDE.Services;
5
6public sealed partial class CSharpLanguageService
7{
8 public sealed record DefinitionLocation(string FilePath, int Line, int Column);
9
10 /// <summary>Go-to-definition target for Roslyn fast path (1-based line/column).</summary>
11 public DefinitionLocation? TryGetDefinitionLocation(
12 string filePath,
13 string sourceText,
14 int line,
15 int column,
16 CancellationToken ct = default)
17 {
18 if (string.IsNullOrEmpty(filePath) || line < 1 || column < 1)
19 return null;
20
21 try
22 {
23 var text = SourceText.From(sourceText);
24 var model = GetOrCreateModel(filePath, text, ct);
25 var lines = text.Lines;
26 if (line > lines.Count)
27 return null;
28
29 var lineInfo = lines[line - 1];
30 var position = lineInfo.Start + Math.Min(Math.Max(0, column - 1), lineInfo.Span.Length);
31 var root = model.SyntaxTree.GetRoot(ct);
32 var token = root.FindToken(position, findInsideTrivia: true);
33 ISymbol? symbol = null;
34 for (var node = token.Parent; node is not null; node = node.Parent)
35 {
36 var info = model.GetSymbolInfo(node, ct);
37 symbol = info.Symbol ?? info.CandidateSymbols.FirstOrDefault();
38 if (symbol is not null)
39 break;
40 }
41
42 if (symbol?.Locations.FirstOrDefault() is not { } location)
43 return null;
44 if (location.SourceTree is null)
45 return null;
46
47 var defPath = location.SourceTree.FilePath;
48 if (string.IsNullOrWhiteSpace(defPath))
49 defPath = filePath;
50
51 var linePos = location.GetLineSpan().StartLinePosition;
52 return new DefinitionLocation(
53 defPath,
54 linePos.Line + 1,
55 linePos.Character + 1);
56 }
57 catch
58 {
59 return null;
60 }
61 }
62}
63
View only · write via MCP/CIDE