Skip to content
Merged
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
2 changes: 2 additions & 0 deletions denops/gin/action/show.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,8 @@ async function doShow(
await denops.dispatch("gin", "buffer:command", "", "", [
`++opener=${opener}`,
...(emojify ? [`++emojify`] : []),
`++diffjump=${x.commit}`,
`++difffold`,
"show",
x.commit,
]);
Expand Down
6 changes: 6 additions & 0 deletions denops/gin/command/buffer/command.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ export type ExecOptions = {
monochrome?: boolean;
opener?: string;
emojify?: boolean;
diffjump?: string;
difffold?: boolean;
filetype?: string;
cmdarg?: string;
mods?: string;
bang?: boolean;
Expand All @@ -33,6 +36,9 @@ export async function exec(
processor: unnullish(options.processor, (v) => v.join(" ")),
monochrome: unnullish(options.monochrome, (v) => v ? "" : undefined),
emojify: unnullish(options.emojify, (v) => v ? "" : undefined),
diffjump: options.diffjump,
difffold: unnullish(options.difffold, (v) => v ? "" : undefined),
filetype: options.filetype ?? "gin-buffer",
},
fragment: `${args.join(" ")}$`,
});
Expand Down
22 changes: 21 additions & 1 deletion denops/gin/command/buffer/edit.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ import {
buildDecorationsFromAnsiEscapeCode,
} from "../../util/ansi_escape_code.ts";
import { execute } from "../../git/executor.ts";
import { init as initDiffJump } from "../../feat/diffjump/jump.ts";
import { init as initDiffFold } from "../../feat/difffold/fold.ts";

export async function edit(
denops: Denops,
Expand All @@ -42,8 +44,23 @@ export async function edit(
monochrome: "monochrome" in (params ?? {}),
encoding: opts.enc ?? opts.encoding,
fileformat: opts.ff ?? opts.fileformat,
filetype: unnullish(
params?.filetype,
(v) => ensure(v, is.String, { message: "filetype must be string" }),
),
emojify: "emojify" in (params ?? {}),
});

// Initialize diff jump functionality if ++diffjump option is present
const jumpCommitish = params?.diffjump;
if (jumpCommitish !== undefined) {
await initDiffJump(denops, bufnr, "buffer");
}

// Initialize diff fold functionality if ++difffold option is present
if ("difffold" in (params ?? {})) {
await initDiffFold(denops, bufnr);
}
}

export type ExecOptions = {
Expand All @@ -52,6 +69,7 @@ export type ExecOptions = {
monochrome?: boolean;
encoding?: string;
fileformat?: string;
filetype?: string;
emojify?: boolean;
stdoutIndicator?: string;
stderrIndicator?: string;
Expand Down Expand Up @@ -110,7 +128,9 @@ export async function exec(
await buffer.concrete(denops, bufnr);
await buffer.ensure(denops, bufnr, async () => {
await batch.batch(denops, async (denops) => {
await option.filetype.setLocal(denops, "gin");
if (options.filetype !== undefined) {
await option.filetype.setLocal(denops, options.filetype);
}
await option.bufhidden.setLocal(denops, "unload");
await option.buftype.setLocal(denops, "nofile");
await option.swapfile.setLocal(denops, false);
Expand Down
43 changes: 42 additions & 1 deletion denops/gin/command/buffer/main.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import type { Denops } from "jsr:@denops/std@^7.0.0";
import { unnullish } from "jsr:@lambdalisue/unnullish@^1.0.0";
import { assert, is } from "jsr:@core/unknownutil@^4.0.0";
import { assert, ensure, is } from "jsr:@core/unknownutil@^4.0.0";
import * as helper from "jsr:@denops/std@^7.0.0/helper";
import * as buffer from "jsr:@denops/std@^7.0.0/buffer";
import * as fn from "jsr:@denops/std@^7.0.0/function";
import { parse as parseBufname } from "jsr:@denops/std@^7.0.0/bufname";
import {
builtinOpts,
formatOpts,
Expand All @@ -14,6 +16,7 @@ import { normCmdArgs, parseSilent } from "../../util/cmd.ts";
import { exec } from "./command.ts";
import { edit } from "./edit.ts";
import { read } from "./read.ts";
import { main as mainDiffJump } from "../../feat/diffjump/main.ts";

export function main(denops: Denops): void {
denops.dispatcher = {
Expand Down Expand Up @@ -47,6 +50,38 @@ export function main(denops: Denops): void {
);
},
};
mainDiffJump(denops, "buffer", {
commitishMap: {
old: async ({ bufnr }) => {
const bufname = await fn.bufname(denops, bufnr);
const { params } = parseBufname(bufname);
const jumpCommitish = params?.diffjump;
if (jumpCommitish === undefined) {
return "HEAD^";
}
const commitish = ensure(
jumpCommitish || "HEAD",
is.String,
{ message: "jump must be string" },
);
return `${commitish}^`;
},
new: async ({ bufnr }) => {
const bufname = await fn.bufname(denops, bufnr);
const { params } = parseBufname(bufname);
const jumpCommitish = params?.diffjump;
if (jumpCommitish === undefined) {
return "HEAD";
}
const commitish = ensure(
jumpCommitish || "HEAD",
is.String,
{ message: "jump must be string" },
);
return commitish;
},
},
});
}
Comment on lines +53 to 85
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

Handle root commits (no parent) in commitishMap

old→${commitish}^ (or "HEAD^") fails on a root commit. Ensure jump gracefully handles no-parent commits (e.g., treat old side as empty) or guard with a parent check.

You can verify if root commits are handled in the diffjump layer:

If not present, consider guarding here or in feat/diffjump to avoid resolving ${commitish}^ when parent count is zero.


🏁 Script executed:

#!/bin/bash
# Look for explicit handling of root/no-parent commits in diffjump implementation
rg -n -C3 -S -g '!**/test/**' --type=ts \
  '(no parent|root commit|^.*parent.*\bcount\b|HEAD\^|[^A-Za-z]zero-parents)' denops

Length of output: 888


Guard against root commits in commitishMap

Appending ^ to a root commit (e.g. HEAD^ or ${commitish}^) will error—detect zero‐parent commits (e.g. via git rev-list --parents) or catch failures and return an empty old revision. Apply this guard in both:

  • denops/gin/command/buffer/main.ts (commitishMap old handler)
  • denops/gin/feat/diffjump/main.ts (default old = "HEAD^")


async function command(
Expand All @@ -62,6 +97,9 @@ async function command(
"monochrome",
"opener",
"emojify",
"diffjump",
"difffold",
"filetype",
...builtinOpts,
]);
return exec(denops, residue, {
Expand All @@ -70,6 +108,9 @@ async function command(
monochrome: unnullish(opts.monochrome, () => true),
opener: opts.opener,
emojify: unnullish(opts.emojify, () => true),
diffjump: opts.diffjump,
difffold: unnullish(opts.difffold, () => true),
filetype: opts.filetype,
cmdarg: formatOpts(opts, builtinOpts).join(" "),
mods,
bang: bang === "!",
Expand Down
5 changes: 1 addition & 4 deletions denops/gin/command/diff/commitish.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,4 @@
export const INDEX = Symbol("INDEX");
export const WORKTREE = Symbol("WORKTREE");

export type Commitish = string | typeof INDEX | typeof WORKTREE;
import { type Commitish, INDEX, WORKTREE } from "../../feat/diffjump/jump.ts";

// git diff
// INDEX -> WORKTREE
Expand Down
3 changes: 2 additions & 1 deletion denops/gin/command/diff/commitish_test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { assertEquals } from "jsr:@std/assert@^1.0.0";
import { Commitish, INDEX, parseCommitish, WORKTREE } from "./commitish.ts";
import { type Commitish, INDEX, WORKTREE } from "../../feat/diffjump/jump.ts";
import { parseCommitish } from "./commitish.ts";

Deno.test("parseCommitish", () => {
const testcases: [[string, boolean], [Commitish, Commitish]][] = [
Expand Down
36 changes: 8 additions & 28 deletions denops/gin/command/diff/edit.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import type { Denops } from "jsr:@denops/std@^7.0.0";
import { unnullish } from "jsr:@lambdalisue/unnullish@^1.0.0";
import { ensure, is } from "jsr:@core/unknownutil@^4.0.0";
import * as batch from "jsr:@denops/std@^7.0.0/batch";
import * as mapping from "jsr:@denops/std@^7.0.0/mapping";
import * as option from "jsr:@denops/std@^7.0.0/option";
import * as vars from "jsr:@denops/std@^7.0.0/variable";
import { parse as parseBufname } from "jsr:@denops/std@^7.0.0/bufname";
Expand All @@ -15,6 +14,8 @@ import {
} from "jsr:@denops/std@^7.0.0/argument";
import * as buffer from "jsr:@denops/std@^7.0.0/buffer";
import { exec as execBuffer } from "../../command/buffer/edit.ts";
import { init as initDiffJump } from "../../feat/diffjump/jump.ts";
import { init as initDiffFold } from "../../feat/difffold/fold.ts";

export async function edit(
denops: Denops,
Expand Down Expand Up @@ -85,33 +86,12 @@ export async function exec(
await option.buftype.setLocal(denops, "nofile");
await option.swapfile.setLocal(denops, false);
await option.modifiable.setLocal(denops, false);
await mapping.map(
denops,
"<Plug>(gin-diffjump-old)",
`<Cmd>call denops#request('gin', 'diff:jump:old', [])<CR>`,
{
buffer: true,
noremap: true,
},
);
await mapping.map(
denops,
"<Plug>(gin-diffjump-new)",
`<Cmd>call denops#request('gin', 'diff:jump:new', [])<CR>`,
{
buffer: true,
noremap: true,
},
);
await mapping.map(
denops,
"<Plug>(gin-diffjump-smart)",
`<Cmd>call denops#request('gin', 'diff:jump:smart', [])<CR>`,
{
buffer: true,
noremap: true,
},
);
});
});

// Initialize diff jump functionality
await initDiffJump(denops, bufnr, "diff");

// Initialize diff fold functionality
await initDiffFold(denops, bufnr);
}
Loading