Skip to content

Commit 2689deb

Browse files
committed
Fix alias-qualified go-to-definition in LSP
1 parent f550114 commit 2689deb

4 files changed

Lines changed: 97 additions & 13 deletions

File tree

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ All notable changes to FScript are documented in this file.
44

55
## [Unreleased]
66

7+
- Fixed LSP go-to-definition for alias-qualified function calls (for example `Helpers.append_part`).
8+
79
## [0.35.0]
810

911
- Fixed LSP completion insertion for dotted prefixes so selecting `Option.map` after `Option.` no longer duplicates the qualifier.

src/FScript.CSharpInterop/LanguageServer/LspHandlers.fs

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -762,18 +762,23 @@ module LspHandlers =
762762
| Some uri, Some (line, character) when documents.ContainsKey(uri) ->
763763
let doc = documents[uri]
764764
let targetTypeName =
765-
match tryResolveSymbol doc line character with
766-
| Some sym ->
767-
match sym.TypeTargetName with
768-
| Some name -> Some name
765+
let fromSymbol =
766+
match tryResolveSymbol doc line character with
767+
| Some sym ->
768+
match sym.TypeTargetName with
769+
| Some name -> Some name
770+
| None ->
771+
sym.TypeText
772+
|> Option.bind (fun t ->
773+
doc.Symbols
774+
|> List.tryFind (fun s -> s.Kind = 5 && s.Name = t)
775+
|> Option.map (fun s -> s.Name))
769776
| None ->
770-
sym.TypeText
771-
|> Option.bind (fun t ->
772-
doc.Symbols
773-
|> List.tryFind (fun s -> s.Kind = 5 && s.Name = t)
774-
|> Option.map (fun s -> s.Name))
775-
| None ->
776-
tryResolveTypeTargetAtPosition doc line character
777+
None
778+
779+
match fromSymbol with
780+
| Some _ -> fromSymbol
781+
| None -> tryResolveTypeTargetAtPosition doc line character
777782

778783
match targetTypeName with
779784
| Some typeName ->

src/FScript.CSharpInterop/LanguageServer/LspSymbols.fs

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1644,8 +1644,19 @@ module LspSymbols =
16441644
| Some word ->
16451645
let candidates =
16461646
if word.Contains('.') then
1647-
let segments = word.Split('.') |> Array.toList
1648-
word :: segments
1647+
let parts = word.Split('.') |> Array.toList
1648+
let mapped =
1649+
match parts with
1650+
| qualifier :: memberName :: [] ->
1651+
match doc.ImportAliasToInternal |> Map.tryFind qualifier with
1652+
| Some internalPrefix ->
1653+
[ $"{internalPrefix}.{memberName}"; internalPrefix ]
1654+
| None ->
1655+
[]
1656+
| _ ->
1657+
[]
1658+
1659+
(word :: parts) @ mapped
16491660
else
16501661
[ word ]
16511662
|> List.distinct

tests/FScript.LanguageServer.Tests/CSharpServerCoreTests.cs

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -471,6 +471,72 @@ [<export>] let summary (project: Common.ProjectInfo) =
471471
}
472472
}
473473

474+
[Test]
475+
public void CSharp_server_navigates_alias_qualified_function_calls()
476+
{
477+
var client = LspClient.StartCSharp();
478+
var tempDir = Path.Combine(Path.GetTempPath(), $"fscript-lsp-alias-functions-{Guid.NewGuid():N}");
479+
Directory.CreateDirectory(tempDir);
480+
try
481+
{
482+
LspTestFixture.Initialize(client);
483+
484+
var helpersPath = Path.Combine(tempDir, "_helpers.fss");
485+
var mainPath = Path.Combine(tempDir, "main.fss");
486+
File.WriteAllText(helpersPath, """
487+
let append_part part acc =
488+
if part = "" then acc else $"{acc} {part}"
489+
""");
490+
491+
var source = """
492+
import "_helpers.fss" as Helpers
493+
494+
let append_build_arg acc key value =
495+
Helpers.append_part $"--build-arg {key}=\"{value}\"" acc
496+
""";
497+
File.WriteAllText(mainPath, source);
498+
499+
var mainUri = new Uri(mainPath).AbsoluteUri;
500+
var helpersUri = new Uri(helpersPath).AbsoluteUri;
501+
502+
var didOpenParams = new JsonObject
503+
{
504+
["textDocument"] = new JsonObject
505+
{
506+
["uri"] = mainUri,
507+
["languageId"] = "fscript",
508+
["version"] = 1,
509+
["text"] = source
510+
}
511+
};
512+
LspClient.SendNotification(client, "textDocument/didOpen", didOpenParams);
513+
_ = LspClient.ReadUntil(client, 10_000, msg => msg["method"]?.GetValue<string>() == "textDocument/publishDiagnostics");
514+
515+
var definitionParams = new JsonObject
516+
{
517+
["textDocument"] = new JsonObject { ["uri"] = mainUri },
518+
["position"] = new JsonObject { ["line"] = 3, ["character"] = 17 }
519+
};
520+
LspClient.SendRequest(client, 84, "textDocument/definition", definitionParams);
521+
var definitionResponse = LspClient.ReadUntil(client, 10_000, msg => msg["id"] is JsonValue idv && idv.TryGetValue<int>(out var id) && id == 84);
522+
523+
var result = definitionResponse["result"] as JsonObject ?? throw new Exception("Expected definition location.");
524+
Assert.That(result["uri"]?.GetValue<string>(), Is.EqualTo(helpersUri));
525+
var range = result["range"] as JsonObject ?? throw new Exception("Expected range.");
526+
var start = range["start"] as JsonObject ?? throw new Exception("Expected start range.");
527+
Assert.That(start["line"]?.GetValue<int>(), Is.EqualTo(0));
528+
}
529+
finally
530+
{
531+
try { LspTestFixture.Shutdown(client); } catch { }
532+
LspClient.Stop(client);
533+
if (Directory.Exists(tempDir))
534+
{
535+
Directory.Delete(tempDir, true);
536+
}
537+
}
538+
}
539+
474540
private static string FindRepoRoot()
475541
{
476542
var current = new DirectoryInfo(AppContext.BaseDirectory);

0 commit comments

Comments
 (0)