Skip to content

Commit d19df7e

Browse files
committed
refactor(lsp): shouldShowParameterHints config hook replaces provider patching
TypeScriptSupport monkey-patched its client's getParameterHints to veto signature help inside callback bodies - fragile and invisible to the framework. registerLanguageServer now accepts an optional shouldShowParameterHints(editor) callback (same family as shouldAutoTrigger / documentFilter / filterDiagnostics): returning false rejects the request, so the provider stays the request owner - the popup is suppressed/dismissed with no fall-through to lower-priority providers (a fallback answering would undo the veto; document-level opt-out via documentFilter still preserves fall-through). The body-scan policy stays entirely in the TS extension.
1 parent d0db089 commit d19df7e

3 files changed

Lines changed: 26 additions & 23 deletions

File tree

src/extensions/default/TypeScriptSupport/main.js

Lines changed: 7 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -351,27 +351,6 @@ define(function (require, exports, module) {
351351
return false;
352352
}
353353

354-
// tsserver treats a whole callback argument INCLUDING its body as "inside the call", so the
355-
// parent call's signature would pop up (and, via the cursor-activity refresh, never dismiss)
356-
// while coding inside a callback. Rejecting in getParameterHints covers every trigger path -
357-
// Ctrl-Space, typed "("/",", and the cursor-activity re-show, which then auto-dismisses an
358-
// already-visible popup when the caret enters the body. Rejecting (rather than declining in
359-
// hasParameterHints) keeps this provider the owner of the request, so the manager dismisses
360-
// the popup instead of falling through to the legacy Tern JS provider. This is vtsls-specific
361-
// policy, so it wraps OUR provider here instead of living in the shared LSP framework
362-
// (intelephense and pyrefly already answer null inside nested bodies on their own).
363-
function _installParameterHintBodyGate(client) {
364-
const provider = client.parameterHints;
365-
const baseGetParameterHints = provider.getParameterHints.bind(provider);
366-
provider.getParameterHints = function (explicit, onCursorActivity) {
367-
const editor = EditorManager.getActiveEditor();
368-
if (editor && _inFunctionBodyInsideArgs(editor)) {
369-
return $.Deferred().reject(null);
370-
}
371-
return baseGetParameterHints(explicit, onCursorActivity);
372-
};
373-
}
374-
375354
async function start() {
376355
if (registered || !canRun()) {
377356
return;
@@ -390,12 +369,17 @@ define(function (require, exports, module) {
390369
languages: SUPPORTED_LANGUAGES,
391370
languageIdMap: LANGUAGE_ID_MAP,
392371
initializationOptions: INITIALIZATION_OPTIONS,
393-
filterDiagnostics: filterDiagnostics
372+
filterDiagnostics: filterDiagnostics,
373+
// tsserver treats a whole callback argument INCLUDING its body as "inside the call" -
374+
// veto signature help there so the parent call's hint doesn't show (or stick around)
375+
// while coding inside a callback like `on("x", () => { | })`.
376+
shouldShowParameterHints: function (editor) {
377+
return !_inFunctionBodyInsideArgs(editor);
378+
}
394379
});
395380
if (client) {
396381
registered = true;
397382
_client = client;
398-
_installParameterHintBodyGate(client);
399383
}
400384
}
401385

src/languageTools/DefaultProviders.js

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -720,6 +720,19 @@ define(function (require, exports, module) {
720720
docPath = editor.document.file._path,
721721
$deferredHints = $.Deferred();
722722

723+
// A language server may VETO signature help at specific cursor positions by supplying a
724+
// `shouldShowParameterHints(editor)` callback (e.g. vtsls suppresses the parent call's
725+
// hint inside callback bodies). Rejecting here (rather than declining in
726+
// hasParameterHints) keeps this provider the owner of the request, so the manager
727+
// dismisses the popup instead of falling through to a lower-priority provider - and the
728+
// cursor-activity refresh then auto-dismisses an already-visible popup when the caret
729+
// moves into a vetoed position.
730+
var config = this.client.config || {};
731+
if (typeof config.shouldShowParameterHints === "function" &&
732+
!config.shouldShowParameterHints(editor)) {
733+
return $deferredHints.reject(null);
734+
}
735+
723736
this.client.requestParameterHints({
724737
filePath: docPath,
725738
cursorPos: pos

src/languageTools/LSPClient.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -835,6 +835,12 @@ define(function (require, exports, module) {
835835
* treats the default null answer as "all diagnostics off").
836836
* @param {function(Array):Array} [config.filterDiagnostics] - server-specific post-filter for
837837
* published diagnostics
838+
* @param {function(Editor):boolean} [config.shouldShowParameterHints] - per-position VETO for
839+
* signature help: return false to reject the request - the popup is suppressed or
840+
* dismissed there and deliberately does NOT fall through to lower-priority providers
841+
* (a fallback answering would undo the veto). For "this document isn't mine, let
842+
* others serve it" use documentFilter instead - that preserves fall-through. E.g.
843+
* vtsls vetoes the parent call's hint inside nested callback bodies.
838844
* @param {string} [config.suppressStderrPattern] - regex source (string, not RegExp - it
839845
* crosses the node connector); stderr lines matching it are dropped from the live
840846
* console log. Opt in for servers that narrate every request on stderr (pyrefly uses

0 commit comments

Comments
 (0)