Skip to content

Boost results matching module/class qualifier in file path - #545

Open
jacktasia wants to merge 2 commits into
masterfrom
qualifier-boost-ranking
Open

jacktasia wants to merge 2 commits into
masterfrom
qualifier-boost-ranking

Conversation

@jacktasia

@jacktasia jacktasia commented Mar 8, 2026

Copy link
Copy Markdown
Owner

Summary

Closes #409.

  • When jumping from a qualified call like Module1.create() or Foo::bar(), extracts the qualifier (Module1 / Foo) from the left context around the symbol
  • Re-ranks results so files whose path contains the qualifier (case-insensitive) appear first
  • Safe by default: no results are ever removed, only reordered. When no qualifier is present, behavior is identical to before

How it works

  1. dumb-jump--extract-qualifier pulls the qualifier from the already-computed left context (handles . and :: separators, excludes .. range operators)
  2. dumb-jump--boost-by-qualifier does a stable partition of results — matching paths first, others after, preserving relative order within each group
  3. The qualifier is threaded through dumb-jump-fetch-resultsdumb-jump-process-results via the existing plist plumbing
  4. Zero additional searches — pure in-memory reordering of existing results

Why file-path matching works

In most languages, module/class names correlate with file names: Elixir Module1module1.ex, Python module1module1.py, Java Module1Module1.java, Ruby Module1module1.rb, etc.

Test plan

  • All 226 existing tests pass (no behavior change for nil qualifiers)
  • New unit tests for dumb-jump--extract-qualifier (dot, double-colon, range exclusion, nil/empty)
  • New unit tests for dumb-jump--boost-by-qualifier (matching paths sorted first, nil is identity)
  • Manual test: open Elixir/Ruby/Python file with multiple modules defining the same function, jump from qualified call, verify correct module's definition appears first

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Result ranking improved by extracting left-context qualifiers (e.g., module/class) and prioritizing jump targets whose paths match that qualifier, yielding more relevant navigation results.
    • Search/jump behavior now considers language/module context when selecting and ordering results.
  • Tests

    • Added unit tests verifying qualifier extraction and qualifier-based result boosting.

When jumping from a qualified call like Module1.create() or Foo::bar(),
extract the qualifier from the left context and re-rank results so that
files whose path contains the qualifier appear first. This is safe by
default: no results are removed, only reordered, and when no qualifier
is present the behavior is unchanged.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Mar 8, 2026

Copy link
Copy Markdown

Walkthrough

Extracts a qualifier from the left context (e.g., module or namespace), threads that qualifier through result-processing, boosts results whose paths contain the qualifier, and exposes the qualifier in xref results for qualifier-aware filtering, sorting, and jumping.

Changes

Cohort / File(s) Summary
Core qualifier logic & xref wiring
dumb-jump.el
Added dumb-jump--extract-qualifier to parse left-context qualifiers and dumb-jump--boost-by-qualifier to reorder results by qualifier match. Threaded :qualifier through result plists, updated dumb-jump-process-results and dumb-jump-handle-results signatures to accept qualifier, and changed xref-backend-definitions to pass (plist-get info :qualifier) into processing.
Tests
test/dumb-jump-test.el
Added dumb-jump--extract-qualifier-test and dumb-jump--boost-by-qualifier-test to validate qualifier extraction and boosting behavior, including nil-qualifier identity case.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: implementing qualifier-based re-ranking to boost results whose paths contain module/class qualifiers.
Linked Issues check ✅ Passed The PR implementation directly addresses issue #409 by extracting module qualifiers from call context and reordering results to prioritize matching module paths.
Out of Scope Changes check ✅ Passed All changes are narrowly scoped to implement qualifier extraction, boosting, and threading—no unrelated modifications found.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch qualifier-boost-ranking

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (1)
test/dumb-jump-test.el (1)

1421-1433: Consider adding test for range operator exclusion.

The PR objectives mention testing "range exclusion" for the .. operator (e.g., Ruby/Elixir ranges like 1..10), but no such test case is present. Adding a test would verify this edge case.

💡 Suggested additional test case
;; Add after line 1427:
  ;; Range operator ".." should not be parsed as qualifier
  (should (null (dumb-jump--extract-qualifier '(:left "1.." :right "10"))))
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@test/dumb-jump-test.el` around lines 1421 - 1433, Add a test case to
dumb-jump--extract-qualifier to ensure the range operator ".." is not treated as
a qualifier: update the existing ert-deftest dumb-jump--extract-qualifier-test
by inserting a check that (dumb-jump--extract-qualifier '(:left "1.." :right
"10")) returns nil (or using (should (null ...))). This targets the function
dumb-jump--extract-qualifier and verifies the range operator exclusion edge case
referenced in the PR.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@dumb-jump.el`:
- Around line 4525-4528: The qualifier boost is applied too early to
match-no-comments via dumb-jump--boost-by-qualifier (wrapping the
dumb-jump-filter-no-start-comments(match-sorted, qualifier)), but later stages
(match-cur-file-front, re-sorts by path/line, and var-to-jump building) undo it;
instead, apply dumb-jump--boost-by-qualifier after the final ordering stage:
remove the current boost call around match-no-comments and call
dumb-jump--boost-by-qualifier on the already-ordered sequence produced by
match-cur-file-front (and ensure the top-level variable matches is constructed
from that boosted order when qualifier is non-nil) so that qualifier-promoted
candidates persist through subsequent re-sorting and when building var-to-jump.

In `@test/dumb-jump-test.el`:
- Around line 1428-1433: The test uses string= to compare the result of
dumb-jump--extract-qualifier against nil, which will error because string=
expects strings; change those assertions to check for nil safely (e.g. use
(should (null (dumb-jump--extract-qualifier ...))) or (should-not
(dumb-jump--extract-qualifier ...))). Update the three failing expectations that
call dumb-jump--extract-qualifier in the test to use null/not instead of string=
so nil results are handled without raising an error.

---

Nitpick comments:
In `@test/dumb-jump-test.el`:
- Around line 1421-1433: Add a test case to dumb-jump--extract-qualifier to
ensure the range operator ".." is not treated as a qualifier: update the
existing ert-deftest dumb-jump--extract-qualifier-test by inserting a check that
(dumb-jump--extract-qualifier '(:left "1.." :right "10")) returns nil (or using
(should (null ...))). This targets the function dumb-jump--extract-qualifier and
verifies the range operator exclusion edge case referenced in the PR.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 50944f3e-39cf-4db0-8f22-426cf2d58a36

📥 Commits

Reviewing files that changed from the base of the PR and between 41b6b9d and 3d2db2d.

📒 Files selected for processing (2)
  • dumb-jump.el
  • test/dumb-jump-test.el

Comment thread dumb-jump.el Outdated
Comment thread test/dumb-jump-test.el Outdated
Apply dumb-jump--boost-by-qualifier after match-cur-file-front instead
of at match-no-comments, so the boost isn't undone by the subsequent
seq-sort on external results. Also use (null ...) instead of
(string= ... nil) in tests for correctness.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@jacktasia

Copy link
Copy Markdown
Owner Author

Addressed both review comments in a41a83c:

  1. Boost placement: Moved dumb-jump--boost-by-qualifier from the match-no-comments stage to after match-cur-file-front is finalized. The seq-sort on external results was indeed undoing the boost.

  2. string= with nil: Switched to (null ...) for nil-returning test cases — string= with nil is undefined behavior even though it happened to pass.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

♻️ Duplicate comments (1)
dumb-jump.el (1)

4579-4606: ⚠️ Potential issue | 🟠 Major

Return the qualifier-boosted ordering to callers.

Line 4579 computes match-qualified, but Line 4606 still returns match-cur-file-front and :results stays unboosted. dumb-jump-handle-results and xref-backend-definitions both read those fields, so non-aggressive jumps still present the old ordering even when a qualifier is present.

Suggested fix
-         (match-cur-file-front
+         (match-cur-file-ordered
           (if (not prefer-external)
               (append
                (seq-filter (lambda (it)
                              (and (> (plist-get it :diff) 0)
                                   (or (string= (plist-get it :path) cur-file)
                                       (string= (plist-get it :path) rel-cur-file))))
                            match-no-comments)
                (seq-filter (lambda (it)
                              (and (<= (plist-get it :diff) 0)
                                   (or (string= (plist-get it :path) cur-file)
                                       (string= (plist-get it :path) rel-cur-file))))
                            match-no-comments)
                (seq-sort #'dumb-jump--candidate-x<y
                          (seq-filter (lambda (it)
                                        (not (or (string= (plist-get it :path) cur-file)
                                                 (string= (plist-get it :path) rel-cur-file))))
                                      match-no-comments)))
             (append
              (seq-sort #'dumb-jump--candidate-x<y
                        (seq-filter (lambda (it)
                                      (not (or (string= (plist-get it :path) cur-file)
                                               (string= (plist-get it :path) rel-cur-file))))
                                    match-no-comments))
              (seq-filter (lambda (it)
                            (or (string= (plist-get it :path) cur-file)
                                (string= (plist-get it :path) rel-cur-file)))
                          match-no-comments))))
-
-         (match-qualified
-          (dumb-jump--boost-by-qualifier match-cur-file-front qualifier))
+         (match-cur-file-front
+          (dumb-jump--boost-by-qualifier match-cur-file-ordered qualifier))

          (matches
           (if (not prefer-external)
               (seq-uniq
                (append
-                (dumb-jump-current-file-results cur-file match-qualified)
-                (dumb-jump-current-file-results rel-cur-file match-qualified)))
-            match-qualified))
+                (dumb-jump-current-file-results cur-file match-cur-file-front)
+                (dumb-jump-current-file-results rel-cur-file match-cur-file-front)))
+            match-cur-file-front))

-    (list :results results
+    (list :results match-cur-file-front
           :do-var-jump do-var-jump
           :var-to-jump var-to-jump
           :match-cur-file-front match-cur-file-front)))
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@dumb-jump.el` around lines 4579 - 4606, The code computes match-qualified
(the qualifier-boosted ordering) but still returns :results and
:match-cur-file-front using the unboosted match-cur-file-front; update the final
plist so callers get the boosted ordering by returning match-qualified for
:results and :match-cur-file-front (leave var-to-jump and do-var-jump logic that
uses matches intact), ensuring dumb-jump-handle-results and
xref-backend-definitions consume the qualifier-boosted ordering.
🧹 Nitpick comments (2)
test/dumb-jump-test.el (2)

1421-1430: Add the .. negative case to the qualifier tests.

This covers the happy paths, but it misses the regression the helper is specifically meant to avoid: treating a range operator as a qualifier separator. Please add one explicit .. fixture that must return nil, otherwise that branch can regress without tripping this test.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@test/dumb-jump-test.el` around lines 1421 - 1430, The test suite for
dumb-jump--extract-qualifier is missing a negative case for the range operator
".."; add an explicit test assertion in dumb-jump--extract-qualifier-test that
passes a left string containing ".." (e.g. '(:left "a..b" :right "()") or
similar) and asserts the result is nil so the function does not treat ".." as a
qualifier separator; update the test alongside the other should (null ...) cases
referencing dumb-jump--extract-qualifier to prevent regressions.

1432-1445: Broaden this to verify the actual boost contract.

Right now this only proves a single exact-case match moves to the front. dumb-jump--boost-by-qualifier is meant to be case-insensitive and stable, so add at least one mixed-case qualifier/path pair and a second matching result to lock in both behaviors.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@test/dumb-jump-test.el` around lines 1432 - 1445, Update the
dumb-jump--boost-by-qualifier test to assert the full boost contract: add at
least one additional result whose :path matches the qualifier in a different
case (e.g., "Module1" vs "module1") and another matching result so you can
assert case-insensitivity and stability; after calling
dumb-jump--boost-by-qualifier with qualifier "module1" assert the first N items
are the matching entries (both the exact-case and mixed-case paths) and that
their relative order is preserved, while non-matching entries remain after them;
keep the existing nil-qualifier identity assertion.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@dumb-jump.el`:
- Around line 4579-4606: The code computes match-qualified (the
qualifier-boosted ordering) but still returns :results and :match-cur-file-front
using the unboosted match-cur-file-front; update the final plist so callers get
the boosted ordering by returning match-qualified for :results and
:match-cur-file-front (leave var-to-jump and do-var-jump logic that uses matches
intact), ensuring dumb-jump-handle-results and xref-backend-definitions consume
the qualifier-boosted ordering.

---

Nitpick comments:
In `@test/dumb-jump-test.el`:
- Around line 1421-1430: The test suite for dumb-jump--extract-qualifier is
missing a negative case for the range operator ".."; add an explicit test
assertion in dumb-jump--extract-qualifier-test that passes a left string
containing ".." (e.g. '(:left "a..b" :right "()") or similar) and asserts the
result is nil so the function does not treat ".." as a qualifier separator;
update the test alongside the other should (null ...) cases referencing
dumb-jump--extract-qualifier to prevent regressions.
- Around line 1432-1445: Update the dumb-jump--boost-by-qualifier test to assert
the full boost contract: add at least one additional result whose :path matches
the qualifier in a different case (e.g., "Module1" vs "module1") and another
matching result so you can assert case-insensitivity and stability; after
calling dumb-jump--boost-by-qualifier with qualifier "module1" assert the first
N items are the matching entries (both the exact-case and mixed-case paths) and
that their relative order is preserved, while non-matching entries remain after
them; keep the existing nil-qualifier identity assertion.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: cd730b7a-0dd9-41f8-93de-9578903f16f6

📥 Commits

Reviewing files that changed from the base of the PR and between 3d2db2d and a41a83c.

📒 Files selected for processing (2)
  • dumb-jump.el
  • test/dumb-jump-test.el

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

jump function doesn't respect a module name

1 participant