-
-
Notifications
You must be signed in to change notification settings - Fork 9
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
11f640c
commit 17a2ee9
Showing
5 changed files
with
214 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,101 @@ | ||
import type { Denops } from "https://deno.land/x/denops_std@v5.0.1/mod.ts"; | ||
import { unnullish } from "https://deno.land/x/unnullish@v1.0.1/mod.ts"; | ||
import * as path from "https://deno.land/std@0.192.0/path/mod.ts"; | ||
import * as option from "https://deno.land/x/denops_std@v5.0.1/option/mod.ts"; | ||
import { systemopen } from "https://deno.land/x/systemopen@v0.2.0/mod.ts"; | ||
import { findWorktreeFromDenops } from "../../git/worktree.ts"; | ||
import { getRemoteURL } from "../../git/remote.ts"; | ||
import { execute } from "../../git/executor.ts"; | ||
import { getHostingService } from "../../git/hosting_service.ts"; | ||
import { yank } from "../../util/yank.ts"; | ||
import { decodeUtf8 } from "../../util/text.ts"; | ||
|
||
export type ExecOptions = { | ||
worktree?: string; | ||
commitish?: string; | ||
filename?: string; | ||
remote?: string; | ||
permanent?: boolean; | ||
yank?: boolean; | ||
echo?: boolean; | ||
}; | ||
|
||
export async function exec( | ||
denops: Denops, | ||
options: ExecOptions = {}, | ||
): Promise<void> { | ||
const url = await getURL(denops, options); | ||
if (options.yank) { | ||
await yank(denops, url.href); | ||
} | ||
if (options.echo) { | ||
await denops.cmd(`echomsg url`, { url: url.href }); | ||
} else { | ||
await systemopen(url.href); | ||
} | ||
} | ||
|
||
async function getURL( | ||
denops: Denops, | ||
options: ExecOptions, | ||
) { | ||
const verbose = await option.verbose.get(denops); | ||
const worktree = await findWorktreeFromDenops(denops, { | ||
worktree: options.worktree, | ||
verbose: !!verbose, | ||
}); | ||
|
||
const remoteURL = await getRemoteURL(denops, options.remote ?? "origin", { | ||
worktree: options.worktree, | ||
}); | ||
const svc = await getHostingService(remoteURL); | ||
if (options.filename) { | ||
const [filename, lineStart, lineEnd] = parseFilename( | ||
worktree, | ||
options.filename, | ||
); | ||
const commitish = options.permanent | ||
? await getCommitishHash( | ||
denops, | ||
worktree, | ||
options.commitish ?? "HEAD", | ||
) | ||
: options.commitish ?? "HEAD"; | ||
return svc.getBlobURL(commitish, filename, { lineStart, lineEnd }); | ||
} else { | ||
return svc.getRootURL(); | ||
} | ||
} | ||
|
||
function parseFilename( | ||
worktree: string, | ||
filename: string, | ||
): [string, number?, number?] { | ||
const relpath = path.isAbsolute(filename) | ||
? path.relative(worktree, filename) | ||
: filename; | ||
const m = relpath?.match(/^(.*?):(\d+)(?::(\d+))?$/); | ||
if (!m) { | ||
return [relpath, undefined, undefined]; | ||
} | ||
return [m[1], unnullish(m[2], Number), unnullish(m[3], Number)]; | ||
} | ||
|
||
async function getCommitishHash( | ||
denops: Denops, | ||
worktree: string, | ||
commitish: string | typeof HEAD, | ||
): Promise<string> { | ||
const { success, stdout, stderr } = await execute(denops, [ | ||
"rev-parse", | ||
commitish.toString(), | ||
], { | ||
worktree: worktree, | ||
stdoutIndicator: "null", | ||
stderrIndicator: "null", | ||
}); | ||
if (!success) { | ||
throw new Error(decodeUtf8(stderr)); | ||
} | ||
return decodeUtf8(stdout).trim(); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
import type { Denops } from "https://deno.land/x/denops_std@v5.0.1/mod.ts"; | ||
import { assert, is } from "https://deno.land/x/unknownutil@v3.2.0/mod.ts#^"; | ||
import * as helper from "https://deno.land/x/denops_std@v5.0.1/helper/mod.ts"; | ||
import { | ||
parseOpts, | ||
validateOpts, | ||
} from "https://deno.land/x/denops_std@v5.0.1/argument/opts.ts"; | ||
import { normCmdArgs, parseSilent } from "../../util/cmd.ts"; | ||
import { exec } from "./command.ts"; | ||
|
||
export function main(denops: Denops): void { | ||
denops.dispatcher = { | ||
...denops.dispatcher, | ||
"browse:command": (mods, args, range) => { | ||
assert(mods, is.String, { message: "mods must be string" }); | ||
assert(args, is.ArrayOf(is.String), { message: "args must be string[]" }); | ||
assert(range, is.TupleOf([is.Number, is.Number] as const), { | ||
message: "range must be [number, number]", | ||
}); | ||
const silent = parseSilent(mods); | ||
return helper.ensureSilent(denops, silent, () => { | ||
return helper.friendlyCall(denops, () => command(denops, args, range)); | ||
}); | ||
}, | ||
}; | ||
} | ||
|
||
async function command( | ||
denops: Denops, | ||
args: string[], | ||
_range: readonly [number, number], | ||
): Promise<void> { | ||
const [opts, residue] = parseOpts(await normCmdArgs(denops, args)); | ||
validateOpts(opts, [ | ||
"worktree", | ||
"remote", | ||
"permanent", | ||
"yank", | ||
"echo", | ||
]); | ||
const [commitish, filename] = parseResidue(residue); | ||
await exec(denops, { | ||
worktree: opts.worktree, | ||
remote: opts.remote, | ||
commitish, | ||
filename, | ||
permanent: "permanent" in opts, | ||
yank: "yank" in opts, | ||
echo: "echo" in opts, | ||
}); | ||
} | ||
|
||
function parseResidue(residue: string[]): [string?, string?] { | ||
// GinBrowse [{options}] | ||
// GinBrowse [{options}] {path} | ||
// GinBrowse [{options}] {commitish} {path} | ||
switch (residue.length) { | ||
case 0: | ||
return [undefined, undefined]; | ||
case 1: | ||
return [undefined, residue[0]]; | ||
case 2: | ||
return [residue[0], residue[1]]; | ||
default: | ||
throw new Error("Invalid number of arguments"); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
if exists('g:loaded_gin_browse') | ||
finish | ||
endif | ||
let g:loaded_gin_browse = 1 | ||
|
||
function! s:command(mods, args, range) abort | ||
if denops#plugin#wait('gin') | ||
return | ||
endif | ||
call denops#request('gin', 'browse:command', [a:mods, a:args, a:range]) | ||
endfunction | ||
|
||
command! -bar -range=% -nargs=* GinBrowse call s:command(<q-mods>, [<f-args>], [<line1>, <line2>]) |