changesCommands

tcmts.vscode.changes.changesCommands · #l72v3wam · term · runs anywhere

The Changes view's commands: the changes a history entry made or a branch has (tcm diff, as a document), resetting the names to an entry, merging a branch into this one, and showing more history.

import { runTcm } from "#codebase/tcmts/vscode/changes";
import type { ChangesNode, ChangesTree } from "#codebase/tcmts/vscode/changes";
import * as vscode from "vscode";

export function changesCommands(tree: ChangesTree, cwd: string | undefined): vscode.Disposable[] {
  const show = async (title: string, args: string[]) => {
    const r = await runTcm(args, cwd);
    const doc = await vscode.workspace.openTextDocument({ content: `# ${title}\n\n${r.out}`, language: "diff" });
    await vscode.window.showTextDocument(doc, { preview: true });
  };
  return [
    // the definitions whose code the changes touched, in VS Code's multi-file diff editor: edits old beside new,
    // additions and removals whole (renames, and dependents only re-hashed, are in the tree: their code is the same)
    vscode.commands.registerCommand("tcm.showChanges", async (x?: ChangesNode) => {
      if (x?.kind !== "entry" && x?.kind !== "branch") return;
      const title = x.kind === "entry" ? `${x.command} (${x.short})` : `${tree.branch || "this branch"} → ${x.name}`;
      const changes = (await tree.changesOf(x)).filter((c) => (c.change === "edited" || c.change === "added" || c.change === "removed") && c.uri);
      if (!changes.length) return vscode.window.showInformationMessage(`${title}: no code changed`);
      const uri = (u: string | undefined) => (u ? vscode.Uri.parse(u) : undefined);
      const label = (path: string) => vscode.Uri.from({ scheme: "tcm", path: `/${path.replaceAll(".", "/")}.ts` });
      const resources = changes.map((c) =>
        c.change === "edited" ? [label(c.path), uri(c.oldUri), uri(c.uri)]
        : c.change === "removed" ? [label(c.path), uri(c.uri), undefined]
        : [label(c.path), undefined, uri(c.uri)],
      );
      return vscode.commands.executeCommand("vscode.changes", title, resources);
    }),
    vscode.commands.registerCommand("tcm.showChangesText", (x?: ChangesNode) => {
      if (x?.kind === "entry") return show(`${x.command} (${x.short})`, ["diff", x.parents[0]!, x.hash]);
      if (x?.kind === "branch") return show(`this branch → ${x.name}`, ["diff", x.name]);
      return show("the last command's changes", ["diff"]);
    }),
    vscode.commands.registerCommand("tcm.resetTo", async (x?: ChangesNode) => {
      if (x?.kind !== "entry") return;
      const ok = await vscode.window.showWarningMessage(
        `Bring back the names as they were after "${x.command}" (${x.short})?`,
        { modal: true, detail: "A new history entry on top: nothing is rewritten, and it can be undone the same way. Code isn't deleted." },
        "Reset",
      );
      if (ok !== "Reset") return;
      const r = await runTcm(["reset", x.hash], cwd);
      if (r.code) vscode.window.showErrorMessage(`tcm reset: ${r.out.trim()}`);
      else vscode.window.showInformationMessage(r.out.trim().split("\n").at(-1) ?? "reset");
    }),
    vscode.commands.registerCommand("tcm.mergeBranch", async (x?: ChangesNode) => {
      if (x?.kind !== "branch" || x.current) return;
      const ok = await vscode.window.showWarningMessage(`Merge ${x.name} into ${tree.branch || "this branch"}?`, { modal: true, detail: `${x.differs} name(s) differ. Conflicts, if any, are listed by tcm todo.` }, "Merge");
      if (ok !== "Merge") return;
      const r = await runTcm(["merge", x.name], cwd);
      if (r.code) vscode.window.showErrorMessage(`tcm merge: ${r.out.trim()}`);
      else vscode.window.showInformationMessage(r.out.trim().split("\n").at(-1) ?? "merged");
    }),
    vscode.commands.registerCommand("tcm.moreHistory", () => {
      tree.limit += 30;
      tree.refresh();
    }),
    vscode.commands.registerCommand("tcm.refreshChanges", () => tree.refresh()),
  ];
}

uses

used by