From 1d5f46949c1d3763411e60e45f53437d7f20bb75 Mon Sep 17 00:00:00 2001 From: Sebastian Pfitzner Date: Wed, 15 Jul 2026 15:47:34 +0000 Subject: [PATCH] fix: offer signatures for methods with no positional arguments Signature help filtered candidate signatures with `length(s.parameters) > arg`, but `parameters` holds only positional parameters. At the first argument position (`arg == 0`) this dropped every method with zero positional parameters, so a keyword-only method like `bar(; x)` was skipped and a sole `baz(; x)` produced no popup at all. At the first position nothing is committed yet, so keep all methods as candidates; narrowing by positional arity still applies once the cursor moves past the first argument. Co-Authored-By: Claude Opus 4.8 --- src/layer_signatures.jl | 7 ++++++- test/test_signatures.jl | 29 +++++++++++++++++++++++++++++ 2 files changed, 35 insertions(+), 1 deletion(-) diff --git a/src/layer_signatures.jl b/src/layer_signatures.jl index a657881..c3b17ea 100644 --- a/src/layer_signatures.jl +++ b/src/layer_signatures.jl @@ -374,6 +374,11 @@ function _get_signature_help(runtime, uri::URI, offset::Int) arg = _fcall_arg_number(x) - filtered = filter(s -> length(s.parameters) > arg, sigs) + # Once the cursor sits at a positional argument beyond the first (`arg > 0`), + # narrow to signatures that actually have a parameter at that position. At the + # first position (`arg == 0`) nothing has been committed yet, so every method + # remains a candidate — including those with no positional parameters at all + # (e.g. `f(; kw)` or `f()`), which must still be offered rather than skipped. + filtered = arg == 0 ? sigs : filter(s -> length(s.parameters) > arg, sigs) return SignatureResult(filtered, 0, arg) end diff --git a/test/test_signatures.jl b/test/test_signatures.jl index 803ceda..269d6a3 100644 --- a/test/test_signatures.jl +++ b/test/test_signatures.jl @@ -285,3 +285,32 @@ end sig = first(result.signatures) @test [p.label for p in sig.parameters] == ["var\"weird arg\"", "normal"] end + +@testitem "Signatures: methods with 0 positional arguments are not skipped" begin + using JuliaWorkspaces: JuliaWorkspace, add_file!, TextFile, SourceText, get_signature_help + using JuliaWorkspaces.URIs2: URI + + function sigs_for(call) + jw = JuliaWorkspace() + uri = URI("file:///sigzero/s.jl") + add_file!(jw, TextFile(uri, SourceText(call, "julia"))) + return get_signature_help(jw, uri, ncodeunits(call)).signatures + end + + # A function with a positional method and a keyword-only method: at the open + # paren all methods are candidates, so both signatures must be offered — the + # keyword-only `bar(; x)` has 0 positional parameters and must not be dropped. + sigs = sigs_for("bar(x) = x\nbar(; x) = bar(x)\nbar(") + labels = [s.label for s in sigs] + @test any(l -> occursin("bar(x)", l), labels) + @test any(l -> occursin("bar(; x)", l), labels) + + # A sole keyword-only method must still produce a popup (0 positional args). + sigs = sigs_for("baz(; x) = x\nbaz(") + @test !isempty(sigs) + @test any(l -> occursin("baz(; x)", l.label), sigs) + + # A method that takes no arguments at all must also be offered at `(`. + sigs = sigs_for("qux() = 1\nqux(") + @test !isempty(sigs) +end