- 
                Notifications
    You must be signed in to change notification settings 
- Fork 726
Fix signature help to prioritize outer calls over inner calls in nested invocations #1949
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
Co-authored-by: jakebailey <[email protected]>
- Remove scanner.SkipTrivia from getApplicableSpanForArguments to avoid including whitespace after arguments - Add position check in getContainingArgumentInfo to ensure outer calls take precedence over inner calls - Most nested call cases now work correctly (markers a, b, c, d, e) - Known issue: marker f (at the end of nested call) still returns null instead of showing outer call - This represents significant progress on the issue but needs further refinement Co-authored-by: jakebailey <[email protected]>
- Add position check in getContainingArgumentInfo to ensure outer calls take precedence when position is outside inner call's argument list - Exclude contextual invocations from position check to preserve correct behavior for callbacks - Add fallback logic to return the last found call if no outer call contains the position - All signature help tests now pass - Nested call test shows correct behavior: outer call takes precedence except when inside inner call's arguments Co-authored-by: jakebailey <[email protected]>
- Remove trailing whitespace - Clarify comment in getApplicableSpanForArguments - Accept baselines for new test cases Co-authored-by: jakebailey <[email protected]>
| // If we didn't find a call that contains the position, return the last call we found. | ||
| // This handles cases where the cursor is at the edge of a call (e.g., right after a parameter). | ||
| return lastArgumentInfo | 
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't understand why we need this.
@copilot is there a test file that shows where this helps? What happens if you remove it?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The fallback logic (lines 645-647) is needed to handle edge cases where the cursor is inside a call's parentheses but outside the computed applicable span. This happens in several scenarios:
- Empty argument lists: foo(/*cursor*/)- the cursor is inside the parentheses but there are no arguments, so the applicable span may not include this position
- After the last argument: foo(arg/*cursor*/)- when trivia/whitespace follows the last argument
Without this fallback, 14 existing tests fail (e.g., TestSignatureHelpJSDocTags where markers like Foo.method1(/*11*/) return null instead of showing signature help).
The fallback ensures that if we find a call while walking up the tree but the position is slightly outside its span (and there's no outer call), we still show that call's signature help rather than returning null. This preserves the existing behavior for non-nested calls while still fixing the nested call precedence issue.
Problem
Signature help had incorrect behavior for nested function calls. When the cursor was positioned after completing an inner call but still within the outer call's arguments, signature help would show the inner call's signature instead of the outer call's signature.
For example, in this code:
The expected behavior is:
foo(outer call takes precedence)bar(inside inner call's arguments)However, the implementation was showing
bar's signature at position e (after"hello"), when it should showfoo's signature since the user has completed typing the inner call and needs to know what the outer call expects.Solution
Added position validation in
getContainingArgumentInfoto check if the cursor is actually within a call's applicable argument span before returning its signature help. When the cursor is outside an inner call's argument list, the algorithm now continues searching up the tree to find an outer call that contains the position.The fix includes:
argumentsSpan.Contains(position)before returning signature help for a callTest Coverage
Added two comprehensive test cases:
TestSignatureHelpApplicableRangeBasic: Validates signature help is not provided outside call boundariesTestSignatureHelpNestedCalls: Validates correct precedence for all positions in the nested call scenario described aboveAll 23 existing signature help tests continue to pass, confirming no regressions were introduced.
Example
Fixes the nested call precedence issues described in the original issue while maintaining backward compatibility with existing signature help behavior.
Original prompt
💡 You can make Copilot smarter by setting up custom instructions, customizing its development environment and configuring Model Context Protocol (MCP) servers. Learn more Copilot coding agent tips in the docs.