Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions src/services/jsDoc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -420,7 +420,7 @@ export function getJSDocParameterNameCompletions(tag: JSDocParameterTag): Comple
const fn = jsdoc.parent;
if (!isFunctionLike(fn)) return [];

return mapDefined(fn.parameters, param => {
return mapDefined(fn.parameters, (param, index) => {
if (!isIdentifier(param.name)) return undefined;

const name = param.name.text;
Expand All @@ -431,7 +431,9 @@ export function getJSDocParameterNameCompletions(tag: JSDocParameterTag): Comple
return undefined;
}

return { name, kind: ScriptElementKind.parameterElement, kindModifiers: "", sortText: Completions.SortText.LocationPriority };
// Use parameter index as sortText suffix to maintain argument order in completions
const sortText = (Completions.SortText.LocationPriority + String(index).padStart(4, "0")) as Completions.SortText;
return { name, kind: ScriptElementKind.parameterElement, kindModifiers: "", sortText };
});
}

Expand Down
28 changes: 25 additions & 3 deletions tests/cases/fourslash/jsdocParameterNameCompletion.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
///<reference path="fourslash.ts" />

// Tests that @param completions are sorted by argument position (not alphabetically).
// See https://github.com/microsoft/TypeScript/issues/20183

/////**
//// * @param /*0*/
//// */
Expand All @@ -22,8 +25,27 @@
//// */
////function i(foo, bar) {}

// sortText uses LocationPriority ("11") + zero-padded index to maintain argument order
const locationPriority = completion.SortText.LocationPriority;
verify.completions(
{ marker: ["0", "3", "4"], exact: ["foo", "bar"] },
{ marker: "1", exact: "bar" },
{ marker: "2", exact: ["canary", "canoodle"] },
{
marker: ["0", "3", "4"],
exact: [
{ name: "foo", sortText: locationPriority + "0000" },
{ name: "bar", sortText: locationPriority + "0001" },
Comment on lines +28 to +35
Copy link

Copilot AI Feb 2, 2026

Choose a reason for hiding this comment

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

completion.SortText is a branded string type; using locationPriority + "0000" produces a plain string, which is not type-compatible with completion.SortText. Consider constructing the expected sortText values as completion.SortText (e.g., via an explicit cast or small helper that returns completion.SortText) to keep the test type-safe and resilient to harness type checking changes.

Copilot uses AI. Check for mistakes.
],
},
{
marker: "1",
// bar is the second parameter (index 1), already documented foo is filtered out
exact: { name: "bar", sortText: locationPriority + "0001" },
},
Comment on lines +39 to +42
Copy link

Copilot AI Feb 2, 2026

Choose a reason for hiding this comment

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

Same type-safety issue here: locationPriority + "0001" results in an unbranded string, but sortText is typed as completion.SortText in the fourslash harness types. Prefer constructing/casting to completion.SortText (or using a helper) for the expected entry.

Copilot uses AI. Check for mistakes.
{
marker: "2",
// canary is index 1, canoodle is index 2 (cat=0, cantaloupe=3 filtered)
exact: [
{ name: "canary", sortText: locationPriority + "0001" },
{ name: "canoodle", sortText: locationPriority + "0002" },
Comment on lines +34 to +48
Copy link

Copilot AI Feb 2, 2026

Choose a reason for hiding this comment

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

Same type-safety issue here: locationPriority + "0001"/"0002" yields string, but the expected sortText type is completion.SortText. Consider casting or using a helper that returns completion.SortText for these concatenations.

Suggested change
{ name: "foo", sortText: locationPriority + "0000" },
{ name: "bar", sortText: locationPriority + "0001" },
],
},
{
marker: "1",
// bar is the second parameter (index 1), already documented foo is filtered out
exact: { name: "bar", sortText: locationPriority + "0001" },
},
{
marker: "2",
// canary is index 1, canoodle is index 2 (cat=0, cantaloupe=3 filtered)
exact: [
{ name: "canary", sortText: locationPriority + "0001" },
{ name: "canoodle", sortText: locationPriority + "0002" },
{ name: "foo", sortText: (locationPriority + "0000") as completion.SortText },
{ name: "bar", sortText: (locationPriority + "0001") as completion.SortText },
],
},
{
marker: "1",
// bar is the second parameter (index 1), already documented foo is filtered out
exact: { name: "bar", sortText: (locationPriority + "0001") as completion.SortText },
},
{
marker: "2",
// canary is index 1, canoodle is index 2 (cat=0, cantaloupe=3 filtered)
exact: [
{ name: "canary", sortText: (locationPriority + "0001") as completion.SortText },
{ name: "canoodle", sortText: (locationPriority + "0002") as completion.SortText },

Copilot uses AI. Check for mistakes.
],
},
);
Loading