Skip to content

Commit bee56fb

Browse files
committed
👍 Add gitGrep and grep curators
1 parent 941ef40 commit bee56fb

File tree

5 files changed

+195
-1
lines changed

5 files changed

+195
-1
lines changed

deno.jsonc

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@
1818
"./builtin/action/systemopen": "./denops/@fall/builtin/action/systemopen.ts",
1919
"./builtin/action/yank": "./denops/@fall/builtin/action/yank.ts",
2020
"./builtin/curator": "./denops/@fall/builtin/curator/mod.ts",
21+
"./builtin/curator/git-grep": "./denops/@fall/builtin/curator/git_grep.ts",
22+
"./builtin/curator/grep": "./denops/@fall/builtin/curator/grep.ts",
2123
"./builtin/curator/noop": "./denops/@fall/builtin/curator/noop.ts",
2224
"./builtin/curator/rg": "./denops/@fall/builtin/curator/rg.ts",
2325
"./builtin/filter": "./denops/@fall/builtin/filter/mod.ts",
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
import * as fn from "jsr:@denops/std@^7.3.0/function";
2+
import { TextLineStream } from "jsr:@std/streams@^1.0.0/text-line-stream";
3+
4+
import { type Curator, defineCurator } from "../../curator.ts";
5+
6+
type GitGrepDetail = {
7+
path: string;
8+
line: number;
9+
column: number;
10+
context: string;
11+
};
12+
13+
const pattern = new RegExp("^(.*?):(\\d+):(\\d+):(.*)$");
14+
15+
export function gitGrep(): Curator<GitGrepDetail> {
16+
return defineCurator<GitGrepDetail>(
17+
async function* (denops, { query }, { signal }) {
18+
const cwd = await fn.getcwd(denops);
19+
const cmd = new Deno.Command("git", {
20+
cwd,
21+
args: [
22+
"grep",
23+
"--color=never",
24+
"--no-heading",
25+
"--full-name",
26+
"--line-number",
27+
"--column",
28+
query,
29+
],
30+
stdin: "null",
31+
stdout: "piped",
32+
stderr: "null",
33+
});
34+
await using proc = cmd.spawn();
35+
const stream = proc.stdout
36+
.pipeThrough(new TextDecoderStream())
37+
.pipeThrough(new TextLineStream());
38+
let id = 0;
39+
for await (const record of stream) {
40+
signal?.throwIfAborted();
41+
const result = parse(record);
42+
if (!result) {
43+
continue;
44+
}
45+
const { path, line, column, context } = result;
46+
yield {
47+
id: id++,
48+
value: `${path}:${line}:${column}:${context}`,
49+
detail: {
50+
path,
51+
line,
52+
column,
53+
context,
54+
},
55+
};
56+
}
57+
},
58+
);
59+
}
60+
61+
function parse(record: string) {
62+
const m = record.match(pattern);
63+
if (!m) return;
64+
const [, path, line, column, context] = m;
65+
return {
66+
path,
67+
line: Number(line),
68+
column: Number(column),
69+
context,
70+
};
71+
}
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
import type { Denops } from "jsr:@denops/std@^7.3.0";
2+
import * as fn from "jsr:@denops/std@^7.3.0/function";
3+
import { TextLineStream } from "jsr:@std/streams@^1.0.0/text-line-stream";
4+
5+
import { type Curator, defineCurator } from "../../curator.ts";
6+
7+
type GrepDetail = {
8+
path: string;
9+
line: number;
10+
context: string;
11+
};
12+
13+
const pattern = new RegExp("^(.*?):(\\d+):(.*)$");
14+
15+
export function grep(): Curator<GrepDetail> {
16+
let root: string;
17+
return defineCurator<GrepDetail>(
18+
async function* (denops, { args, query }, { signal }) {
19+
root ??= await getAbsolutePathOf(denops, args[0] ?? ".", signal);
20+
const cmd = new Deno.Command("grep", {
21+
args: [
22+
"--color=never",
23+
"--no-messages",
24+
"--recursive",
25+
"--line-number",
26+
query,
27+
"--",
28+
root,
29+
],
30+
stdin: "null",
31+
stdout: "piped",
32+
stderr: "null",
33+
});
34+
await using proc = cmd.spawn();
35+
const stream = proc.stdout
36+
.pipeThrough(new TextDecoderStream())
37+
.pipeThrough(new TextLineStream());
38+
let id = 0;
39+
for await (const record of stream) {
40+
signal?.throwIfAborted();
41+
const result = parse(record);
42+
if (!result) {
43+
continue;
44+
}
45+
const { path, line, context } = result;
46+
yield {
47+
id: id++,
48+
value: `${path}:${line}:${context}`,
49+
detail: {
50+
path: path,
51+
line,
52+
context,
53+
},
54+
};
55+
}
56+
},
57+
);
58+
}
59+
60+
async function getAbsolutePathOf(
61+
denops: Denops,
62+
expr: string,
63+
signal?: AbortSignal,
64+
): Promise<string> {
65+
const path = await fn.expand(denops, expr) as string;
66+
signal?.throwIfAborted();
67+
const abspath = await fn.fnamemodify(denops, path, ":p");
68+
return abspath;
69+
}
70+
71+
function parse(record: string) {
72+
const m = record.match(pattern);
73+
if (!m) return;
74+
const [, path, line, context] = m;
75+
return {
76+
path,
77+
line: Number(line),
78+
context,
79+
};
80+
}
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
11
// This file is generated by gen-mod.ts
2+
export * from "./git_grep.ts";
3+
export * from "./grep.ts";
24
export * from "./noop.ts";
35
export * from "./rg.ts";

denops/fall/_assets/default.config.ts

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,12 +23,51 @@ export const main: Entrypoint = (
2323

2424
defineItemPickerFromCurator(
2525
"grep",
26+
pipeProjectors(
27+
builtin.curator.grep,
28+
builtin.modifier.relativePath,
29+
),
30+
{
31+
previewer: builtin.previewer.file,
32+
actions: {
33+
...quickfixActions,
34+
...builtin.action.defaultOpenActions,
35+
...builtin.action.defaultCdActions,
36+
...builtin.action.defaultEchoActions,
37+
...builtin.action.defaultSystemopenActions,
38+
...builtin.action.defaultSubmatchActions,
39+
},
40+
defaultAction: "open",
41+
},
42+
);
43+
44+
defineItemPickerFromCurator(
45+
"git-grep",
46+
pipeProjectors(
47+
builtin.curator.gitGrep,
48+
builtin.modifier.relativePath,
49+
),
50+
{
51+
previewer: builtin.previewer.file,
52+
actions: {
53+
...quickfixActions,
54+
...builtin.action.defaultOpenActions,
55+
...builtin.action.defaultCdActions,
56+
...builtin.action.defaultEchoActions,
57+
...builtin.action.defaultSystemopenActions,
58+
...builtin.action.defaultSubmatchActions,
59+
},
60+
defaultAction: "open",
61+
},
62+
);
63+
64+
defineItemPickerFromCurator(
65+
"rg",
2666
pipeProjectors(
2767
builtin.curator.rg,
2868
builtin.modifier.relativePath,
2969
),
3070
{
31-
renderer: builtin.renderer.smartPath,
3271
previewer: builtin.previewer.file,
3372
actions: {
3473
...quickfixActions,

0 commit comments

Comments
 (0)