From f533a47d65e43c1577f35291738124b898e0f3a8 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Sat, 9 Aug 2025 04:38:23 -0700 Subject: [PATCH 1/5] Fix commit generator hash length Fixes #260748 --- .../src/completions/upstream/git.ts | 120 +++++++++--------- 1 file changed, 63 insertions(+), 57 deletions(-) diff --git a/extensions/terminal-suggest/src/completions/upstream/git.ts b/extensions/terminal-suggest/src/completions/upstream/git.ts index 8f84acf54f488..f08779a2841e1 100644 --- a/extensions/terminal-suggest/src/completions/upstream/git.ts +++ b/extensions/terminal-suggest/src/completions/upstream/git.ts @@ -29,7 +29,7 @@ const postProcessTrackedFiles: Fig.Generator["postProcess"] = ( try { ext = file.split(".").slice(-1)[0]; - } catch (e) {} + } catch (e) { } if (file.endsWith("/")) { ext = "folder"; @@ -53,67 +53,67 @@ interface PostProcessBranchesOptions { const postProcessBranches = (options: PostProcessBranchesOptions = {}): Fig.Generator["postProcess"] => - (out) => { - const { insertWithoutRemotes = false } = options; + (out) => { + const { insertWithoutRemotes = false } = options; - const output = filterMessages(out); + const output = filterMessages(out); - if (output.startsWith("fatal:")) { - return []; - } + if (output.startsWith("fatal:")) { + return []; + } - const seen = new Set(); - return output - .split("\n") - .filter((line) => !line.trim().startsWith("HEAD")) - .map((branch) => { - let name = branch.trim(); - const parts = branch.match(/\S+/g); - if (parts && parts.length > 1) { - if (parts[0] === "*") { - // We are in a detached HEAD state - if (branch.includes("HEAD detached")) { - return null; + const seen = new Set(); + return output + .split("\n") + .filter((line) => !line.trim().startsWith("HEAD")) + .map((branch) => { + let name = branch.trim(); + const parts = branch.match(/\S+/g); + if (parts && parts.length > 1) { + if (parts[0] === "*") { + // We are in a detached HEAD state + if (branch.includes("HEAD detached")) { + return null; + } + // Current branch + return { + name: branch.replace("*", "").trim(), + description: "Current branch", + priority: 100, + icon: "⭐️", + }; + } else if (parts[0] === "+") { + // Branch checked out in another worktree. + name = branch.replace("+", "").trim(); } - // Current branch - return { - name: branch.replace("*", "").trim(), - description: "Current branch", - priority: 100, - icon: "⭐️", - }; - } else if (parts[0] === "+") { - // Branch checked out in another worktree. - name = branch.replace("+", "").trim(); } - } - let description = "Branch"; + let description = "Branch"; - if (insertWithoutRemotes && name.startsWith("remotes/")) { - name = name.slice(name.indexOf("/", 8) + 1); - description = "Remote branch"; - } + if (insertWithoutRemotes && name.startsWith("remotes/")) { + name = name.slice(name.indexOf("/", 8) + 1); + description = "Remote branch"; + } - const space = name.indexOf(" "); - if (space !== -1) { - name = name.slice(0, space); - } + const space = name.indexOf(" "); + if (space !== -1) { + name = name.slice(0, space); + } - return { - name, - description, - icon: "fig://icon?type=git", - priority: 75, - }; - }) - .filter((suggestion) => { - if (!suggestion) return false; - if (seen.has(suggestion.name)) return false; - seen.add(suggestion.name); - return true; - }); - }; + return { + name, + description, + icon: "fig://icon?type=git", + priority: 75, + }; + }) + .filter((suggestion) => { + if (!suggestion) return false; + if (seen.has(suggestion.name)) return false; + seen.add(suggestion.name); + return true; + }); + }; export const gitGenerators: Record = { // Commit history @@ -126,11 +126,17 @@ export const gitGenerators: Record = { return []; } - return output.split("\n").map((line) => { + const lines = output.split("\n"); + const firstLine = lines.length > 0 ? lines[0] : undefined; + const hashLength = + firstLine && firstLine.length > 0 ? firstLine.indexOf(" ") : 7; + const descriptionStart = hashLength + 1; + + return lines.map((line) => { return { - name: line.substring(0, 7), + name: line.substring(0, hashLength), icon: "fig://icon?type=node", - description: line.substring(7), + description: line.substring(descriptionStart), }; }); }, @@ -426,7 +432,7 @@ export const gitGenerators: Record = { let ext = ""; try { ext = file.split(".").slice(-1)[0]; - } catch (e) {} + } catch (e) { } if (file.endsWith("/")) { ext = "folder"; From 4baaa60c42a6169b187215616998decfefee04e3 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Sat, 9 Aug 2025 05:41:02 -0700 Subject: [PATCH 2/5] Move git spec out of upstream/ --- .../src/completions/{upstream => }/git.ts | 93 +++++++++++-------- extensions/terminal-suggest/src/constants.ts | 1 - .../src/terminalSuggestMain.ts | 16 ++-- 3 files changed, 61 insertions(+), 49 deletions(-) rename extensions/terminal-suggest/src/completions/{upstream => }/git.ts (97%) diff --git a/extensions/terminal-suggest/src/completions/upstream/git.ts b/extensions/terminal-suggest/src/completions/git.ts similarity index 97% rename from extensions/terminal-suggest/src/completions/upstream/git.ts rename to extensions/terminal-suggest/src/completions/git.ts index f08779a2841e1..daadc6b19a139 100644 --- a/extensions/terminal-suggest/src/completions/upstream/git.ts +++ b/extensions/terminal-suggest/src/completions/git.ts @@ -1,3 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +/* eslint-disable local/code-no-unexternalized-strings */ + function ai(...args: any[]): undefined { return undefined; } const filterMessages = (out: string): string => { @@ -80,7 +87,7 @@ const postProcessBranches = name: branch.replace("*", "").trim(), description: "Current branch", priority: 100, - icon: "⭐️", + icon: "⭐️", // allow-any-unicode-next-line }; } else if (parts[0] === "+") { // Branch checked out in another worktree. @@ -108,8 +115,12 @@ const postProcessBranches = }; }) .filter((suggestion) => { - if (!suggestion) return false; - if (seen.has(suggestion.name)) return false; + if (!suggestion) { + return false; + } + if (seen.has(suggestion.name)) { + return false; + } seen.add(suggestion.name); return true; }); @@ -347,7 +358,7 @@ export const gitGenerators: Record = { postProcess: function (output) { return output.split("\n").map((tag) => ({ name: tag, - icon: "🏷️", + icon: "🏷️", // allow-any-unicode-next-line })); }, }, @@ -3857,7 +3868,7 @@ const addOptions: Fig.Option[] = [ { name: ["-n", "--dry-run"], description: - "Don’t actually add the file(s), just show if they exist and/or will be ignored", + "Don't actually add the file(s), just show if they exist and/or will be ignored", }, { name: ["-v", "--verbose"], description: "Be verbose" }, { @@ -3867,7 +3878,7 @@ const addOptions: Fig.Option[] = [ { name: ["-i", "--interactive"], description: - "Add modified contents in the working tree interactively to the index. Optional path arguments may be supplied to limit operation to a subset of the working tree. See “Interactive mode” for details", + "Add modified contents in the working tree interactively to the index. Optional path arguments may be supplied to limit operation to a subset of the working tree. See \"Interactive mode\" for details", }, { name: ["-p", "--patch"], @@ -3902,7 +3913,7 @@ const addOptions: Fig.Option[] = [ { name: "--refresh", description: - "Don’t add the file(s), but only refresh their stat() information in the index", + "Don't add the file(s), but only refresh their stat() information in the index", }, { name: "--ignore-errors", @@ -3958,7 +3969,7 @@ const addOptions: Fig.Option[] = [ const headSuggestions = [ { name: "HEAD", - icon: "🔻", + icon: "🔻", // allow-any-unicode-next-line description: "The most recent commit", }, { @@ -4111,7 +4122,7 @@ const completionSpec: Fig.Spec = { }, { name: "--html-path", - description: "Print Git’s HTML documentation path", + description: "Print Git's HTML documentation path", }, { name: "--man-path", @@ -4924,7 +4935,7 @@ const completionSpec: Fig.Spec = { suggestCurrentToken: true, suggestions: configSuggestions.map((suggestion) => ({ ...suggestion, - icon: "⚙️", + icon: "⚙️", // allow-any-unicode-next-line })), generators: { script: ["git", "config", "--get-regexp", ".*"], @@ -4943,7 +4954,7 @@ const completionSpec: Fig.Spec = { line.startsWith("remote.") || !configSuggestions.find(({ name }) => line === name) ) - .map((name) => ({ name, icon: "⚙️" })), + .map((name) => ({ name, icon: "⚙️" })), // allow-any-unicode-next-line }, }, { @@ -4967,7 +4978,7 @@ const completionSpec: Fig.Spec = { { name: "--keep-base", description: - "Set the starting point at which to create the new commits to the merge base of . Running git rebase --keep-base is equivalent to running git rebase --onto …​ . This option is useful in the case where one is developing a feature on top of an upstream branch. While the feature is being worked on, the upstream branch may advance and it may not be the best idea to keep rebasing on top of the upstream but to keep the base commit as-is. Although both this option and --fork-point find the merge base between and , this option uses the merge base as the starting point on which new commits will be created, whereas --fork-point uses the merge base to determine the set of commits which will be rebased", + "Set the starting point at which to create the new commits to the merge base of . Running git rebase --keep-base is equivalent to running git rebase --onto ... . This option is useful in the case where one is developing a feature on top of an upstream branch. While the feature is being worked on, the upstream branch may advance and it may not be the best idea to keep rebasing on top of the upstream but to keep the base commit as-is. Although both this option and --fork-point find the merge base between and , this option uses the merge base as the starting point on which new commits will be created, whereas --fork-point uses the merge base to determine the set of commits which will be rebased", }, { name: "--continue", @@ -5001,12 +5012,12 @@ const completionSpec: Fig.Spec = { { name: "--no-keep-empty", description: - "Do not keep commits that start empty before the rebase (i.e. that do not change anything from its parent) in the result. The default is to keep commits which start empty, since creating such commits requires passing the --allow-empty override flag to git commit, signifying that a user is very intentionally creating such a commit and thus wants to keep it. Usage of this flag will probably be rare, since you can get rid of commits that start empty by just firing up an interactive rebase and removing the lines corresponding to the commits you don’t want. This flag exists as a convenient shortcut, such as for cases where external tools generate many empty commits and you want them all removed. For commits which do not start empty but become empty after rebasing, see the --empty flag", + "Do not keep commits that start empty before the rebase (i.e. that do not change anything from its parent) in the result. The default is to keep commits which start empty, since creating such commits requires passing the --allow-empty override flag to git commit, signifying that a user is very intentionally creating such a commit and thus wants to keep it. Usage of this flag will probably be rare, since you can get rid of commits that start empty by just firing up an interactive rebase and removing the lines corresponding to the commits you don't want. This flag exists as a convenient shortcut, such as for cases where external tools generate many empty commits and you want them all removed. For commits which do not start empty but become empty after rebasing, see the --empty flag", }, { name: "--keep-empty", description: - "Keep commits that start empty before the rebase (i.e. that do not change anything from its parent) in the result. The default is to keep commits which start empty, since creating such commits requires passing the --allow-empty override flag to git commit, signifying that a user is very intentionally creating such a commit and thus wants to keep it. Usage of this flag will probably be rare, since you can get rid of commits that start empty by just firing up an interactive rebase and removing the lines corresponding to the commits you don’t want. This flag exists as a convenient shortcut, such as for cases where external tools generate many empty commits and you want them all removed. For commits which do not start empty but become empty after rebasing, see the --empty flag", + "Keep commits that start empty before the rebase (i.e. that do not change anything from its parent) in the result. The default is to keep commits which start empty, since creating such commits requires passing the --allow-empty override flag to git commit, signifying that a user is very intentionally creating such a commit and thus wants to keep it. Usage of this flag will probably be rare, since you can get rid of commits that start empty by just firing up an interactive rebase and removing the lines corresponding to the commits you don't want. This flag exists as a convenient shortcut, such as for cases where external tools generate many empty commits and you want them all removed. For commits which do not start empty but become empty after rebasing, see the --empty flag", }, { name: "--reapply-cherry-picks", @@ -5214,12 +5225,12 @@ const completionSpec: Fig.Spec = { { name: "--autosquash", description: - "When the commit log message begins with 'squash! …​' (or 'fixup! …​'), and there is already a commit in the todo list that matches the same ..., automatically modify the todo list of rebase -i so that the commit marked for squashing comes right after the commit to be modified, and change the action of the moved commit from pick to squash (or fixup). A commit matches the ... if the commit subject matches, or if the ... refers to the commit’s hash. As a fall-back, partial matches of the commit subject work, too. The recommended way to create fixup/squash commits is by using the --fixup/--squash options of git-commit[1]", + "When the commit log message begins with 'squash!' (or 'fixup!'), and there is already a commit in the todo list that matches the same ..., automatically modify the todo list of rebase -i so that the commit marked for squashing comes right after the commit to be modified, and change the action of the moved commit from pick to squash (or fixup). A commit matches the ... if the commit subject matches, or if the ... refers to the commit's hash. As a fall-back, partial matches of the commit subject work, too. The recommended way to create fixup/squash commits is by using the --fixup/--squash options of git-commit[1]", }, { name: "--no-autosquash", description: - "When the commit log message begins with 'squash! …' (or 'fixup! …'), and there is already a commit in the todo list that matches the same ..., automatically modify the todo list of rebase -i so that the commit marked for squashing comes right after the commit to be modified, and change the action of the moved commit from pick to squash (or fixup). A commit matches the ... if the commit subject matches, or if the ... refers to the commit’s hash. As a fall-back, partial matches of the commit subject work, too. The recommended way to create fixup/squash commits is by using the --fixup/--squash options of git-commit[1]", + "When the commit log message begins with 'squash!' (or 'fixup!'), and there is already a commit in the todo list that matches the same ..., automatically modify the todo list of rebase -i so that the commit marked for squashing comes right after the commit to be modified, and change the action of the moved commit from pick to squash (or fixup). A commit matches the ... if the commit subject matches, or if the ... refers to the commit's hash. As a fall-back, partial matches of the commit subject work, too. The recommended way to create fixup/squash commits is by using the --fixup/--squash options of git-commit[1]", }, { name: "--autostash", @@ -5488,7 +5499,7 @@ const completionSpec: Fig.Spec = { { name: ["-n", "--dry-run"], description: - "Don’t actually remove anything, just show what would be done", + "Don't actually remove anything, just show what would be done", }, { name: ["-q", "--quiet"], @@ -5506,7 +5517,7 @@ const completionSpec: Fig.Spec = { { name: "-x", description: - "Don’t use the standard ignore rules (see gitignore(5)), but still use the ignore rules given with -e options from the command line. This allows removing all untracked files, including build products. This can be used (possibly in conjunction with git restore or git reset) to create a pristine working directory to test a clean build", + "Don't use the standard ignore rules (see gitignore(5)), but still use the ignore rules given with -e options from the command line. This allows removing all untracked files, including build products. This can be used (possibly in conjunction with git restore or git reset) to create a pristine working directory to test a clean build", }, { name: "-X", @@ -5727,7 +5738,7 @@ const completionSpec: Fig.Spec = { name: ["--rebase", "-r"], isDangerous: true, description: - "Fetch the remote’s copy of current branch and rebases it into the local copy", + "Fetch the remote's copy of current branch and rebases it into the local copy", args: { isOptional: true, name: "remote", @@ -5819,7 +5830,7 @@ const completionSpec: Fig.Spec = { { name: "--signoff", description: - "Add a Signed-off-by trailer by the committer at the end of the commit log message. The meaning of a signoff depends on the project to which you’re committing. For example, it may certify that the committer has the rights to submit the work under the project’s license or agrees to some contributor representation, such as a Developer Certificate of Origin. (See http://developercertificate.org for the one used by the Linux kernel and Git projects.) Consult the documentation or leadership of the project to which you’re contributing to understand how the signoffs are used in that project", + "Add a Signed-off-by trailer by the committer at the end of the commit log message. The meaning of a signoff depends on the project to which you're committing. For example, it may certify that the committer has the rights to submit the work under the project's license or agrees to some contributor representation, such as a Developer Certificate of Origin. (See http://developercertificate.org for the one used by the Linux kernel and Git projects.) Consult the documentation or leadership of the project to which you're contributing to understand how the signoffs are used in that project", }, { name: "--no-signoff", @@ -6355,7 +6366,7 @@ const completionSpec: Fig.Spec = { { name: "-m", description: - "A symbolic-ref refs/remotes//HEAD is set up to point at remote’s branch", + "A symbolic-ref refs/remotes//HEAD is set up to point at remote's branch", args: { name: "master", }, @@ -6784,7 +6795,7 @@ const completionSpec: Fig.Spec = { name: "path", }, description: - 'Prepend to paths printed in informative messages such as ”Fetching submodule foo". This option is used internally when recursing over submodules', + 'Prepend to paths printed in informative messages such as "Fetching submodule foo". This option is used internally when recursing over submodules', }, { name: "--recurse-submodules-default", @@ -7161,7 +7172,7 @@ const completionSpec: Fig.Spec = { { name: "--server-option", description: - "Transmit the given string to the server when communicating using protocol version 2. The given string must not contain a NUL or LF character. The server’s handling of server options, including unknown ones, is server-specific. When multiple --server-option=