@xynogen/pix-find 0.1.27 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +6 -3
- package/src/extension.ts +4 -2
- package/src/find.test.ts +24 -5
- package/src/find.ts +21 -1
package/package.json
CHANGED
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xynogen/pix-find",
|
|
3
|
-
"version": "0.1
|
|
3
|
+
"version": "0.2.1",
|
|
4
4
|
"description": "Pi tool — glob file search",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "src/index.ts",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": "./src/index.ts",
|
|
9
|
+
"./extension": "./src/extension.ts"
|
|
10
|
+
},
|
|
7
11
|
"scripts": {
|
|
8
12
|
"test": "bun test"
|
|
9
13
|
},
|
|
@@ -34,9 +38,8 @@
|
|
|
34
38
|
"access": "public"
|
|
35
39
|
},
|
|
36
40
|
"dependencies": {
|
|
37
|
-
"@xynogen/pix-data": "^0.4.3",
|
|
38
41
|
"@xynogen/pix-pretty": "^1.11.2",
|
|
39
|
-
"@xynogen/pix-runtime": "^0.
|
|
42
|
+
"@xynogen/pix-runtime": "^0.8.0"
|
|
40
43
|
},
|
|
41
44
|
"peerDependencies": {
|
|
42
45
|
"@earendil-works/pi-coding-agent": "*"
|
package/src/extension.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
createFindToolDefinition,
|
|
3
3
|
createFindTool as createFindToolFallback,
|
|
4
|
+
type ExtensionAPI,
|
|
4
5
|
type FindToolInput,
|
|
5
6
|
} from "@earendil-works/pi-coding-agent";
|
|
6
7
|
import { CursorStore, fffState } from "@xynogen/pix-pretty/fff";
|
|
@@ -9,7 +10,8 @@ import { shortPath, viewportTextConstructor } from "@xynogen/pix-pretty/utils";
|
|
|
9
10
|
import { once } from "@xynogen/pix-runtime/once";
|
|
10
11
|
import { registerFindTool } from "./find.js";
|
|
11
12
|
|
|
12
|
-
export default function pixFindExtension(pi:
|
|
13
|
+
export default function pixFindExtension(pi: ExtensionAPI): void {
|
|
14
|
+
const prettyPi = pi as unknown as PiPrettyApi;
|
|
13
15
|
once(pi, "pix-find", () => {
|
|
14
16
|
const createFindTool = (createFindToolDefinition ??
|
|
15
17
|
createFindToolFallback) as unknown as ToolFactory<FindToolInput>;
|
|
@@ -25,7 +27,7 @@ export default function pixFindExtension(pi: PiPrettyApi): void {
|
|
|
25
27
|
const cwd = process.cwd();
|
|
26
28
|
const home = process.env.HOME ?? "";
|
|
27
29
|
|
|
28
|
-
registerFindTool(
|
|
30
|
+
registerFindTool(prettyPi, createFindTool, {
|
|
29
31
|
cwd,
|
|
30
32
|
sp: (p: string) => shortPath(cwd, home, p),
|
|
31
33
|
TextComponent: viewportTextConstructor(TextComponent),
|
package/src/find.test.ts
CHANGED
|
@@ -6,7 +6,24 @@ import type {
|
|
|
6
6
|
TextComponentCtor,
|
|
7
7
|
ThemeLike,
|
|
8
8
|
} from "@xynogen/pix-pretty/types";
|
|
9
|
-
import { applyFindDefaults, DEFAULT_FIND_LIMIT, registerFindTool } from "./find";
|
|
9
|
+
import { applyFindDefaults, DEFAULT_FIND_LIMIT, globHighlight, registerFindTool } from "./find";
|
|
10
|
+
|
|
11
|
+
describe("globHighlight", () => {
|
|
12
|
+
it("keeps literal runs from a glob as case-insensitive alternatives", () => {
|
|
13
|
+
const re = globHighlight("**/*.test.ts");
|
|
14
|
+
expect(re).toBeInstanceOf(RegExp);
|
|
15
|
+
expect((re as RegExp).flags).toBe("gi");
|
|
16
|
+
expect("foo.test.ts".match(re as RegExp)).not.toBeNull();
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
it("highlights the extension of a simple glob", () => {
|
|
20
|
+
expect("app.ts".match(globHighlight("*.ts") as RegExp)?.[0]).toBe(".ts");
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
it("returns undefined when the glob has no meaningful literal run", () => {
|
|
24
|
+
expect(globHighlight("*")).toBeUndefined();
|
|
25
|
+
});
|
|
26
|
+
});
|
|
10
27
|
|
|
11
28
|
class MockTextComponent {
|
|
12
29
|
private text = "";
|
|
@@ -108,10 +125,12 @@ describe("registerFindTool", () => {
|
|
|
108
125
|
} as unknown as RenderContextLike,
|
|
109
126
|
);
|
|
110
127
|
|
|
111
|
-
|
|
112
|
-
|
|
128
|
+
const strip = (s: string) => s.replace(/\u001b\[[0-9;]*m/g, "");
|
|
129
|
+
const shown = strip(result?.getText() ?? "");
|
|
130
|
+
expect(shown).toContain("src/one.ts");
|
|
131
|
+
expect(shown).toContain("src/two.ts");
|
|
113
132
|
// No floating count header in the framed view — the collapsed row carries it.
|
|
114
|
-
expect(
|
|
133
|
+
expect(shown).not.toContain("2 files");
|
|
115
134
|
});
|
|
116
135
|
|
|
117
136
|
it("frames single-file output like multi-file (no inline row)", () => {
|
|
@@ -156,7 +175,7 @@ describe("registerFindTool", () => {
|
|
|
156
175
|
?.getText() ?? "";
|
|
157
176
|
// Single result is framed just like multi — one shape, no inline row.
|
|
158
177
|
expect(out).toContain("─");
|
|
159
|
-
expect(out).toContain("src/a.ts");
|
|
178
|
+
expect(strip(out)).toContain("src/a.ts");
|
|
160
179
|
expect(strip(out)).not.toContain("file");
|
|
161
180
|
const multi = {
|
|
162
181
|
content: [{ type: "text", text: "a.ts\nb.ts\nc.ts" }],
|
package/src/find.ts
CHANGED
|
@@ -34,6 +34,25 @@ export function applyFindDefaults(params: FindParams): FindParams {
|
|
|
34
34
|
return params.limit === undefined ? { ...params, limit: DEFAULT_FIND_LIMIT } : params;
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
+
/**
|
|
38
|
+
* Build a highlight regex from a glob pattern by keeping only its literal runs
|
|
39
|
+
* (the wildcard-free fragments) as case-insensitive alternatives. `**/*.test.ts`
|
|
40
|
+
* → highlight `.test.ts`; `*.ts` → highlight `.ts`. A pattern with no literal
|
|
41
|
+
* run (e.g. `*`) yields undefined — nothing meaningful to emphasize.
|
|
42
|
+
*/
|
|
43
|
+
export function globHighlight(pattern: string): RegExp | undefined {
|
|
44
|
+
const literals = pattern
|
|
45
|
+
.split(/[*?{}[\],/]+/) // split on glob metacharacters + path separators
|
|
46
|
+
.filter((s) => s.length >= 2); // skip single chars/dots — too noisy
|
|
47
|
+
if (literals.length === 0) return undefined;
|
|
48
|
+
const alt = literals.map((s) => s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")).join("|");
|
|
49
|
+
try {
|
|
50
|
+
return new RegExp(alt, "gi");
|
|
51
|
+
} catch {
|
|
52
|
+
return undefined;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
37
56
|
export function registerFindTool(
|
|
38
57
|
pi: PiPrettyApi,
|
|
39
58
|
createFindTool: ToolFactory<FindToolInput>,
|
|
@@ -138,7 +157,7 @@ export function registerFindTool(
|
|
|
138
157
|
return text;
|
|
139
158
|
text.setText(
|
|
140
159
|
fillToolBackground(
|
|
141
|
-
`${theme.fg("toolTitle", theme.bold("find"))} ${theme.fg("
|
|
160
|
+
`${theme.fg("toolTitle", theme.bold("find"))} ${theme.fg("muted", pattern)}${path}`,
|
|
142
161
|
),
|
|
143
162
|
);
|
|
144
163
|
return text;
|
|
@@ -193,6 +212,7 @@ export function registerFindTool(
|
|
|
193
212
|
renderDimPreview(output, theme, {
|
|
194
213
|
frame: true,
|
|
195
214
|
paint: (s: string) => theme.fg(renderCtx.isError ? "error" : "success", s),
|
|
215
|
+
highlight: d?._type === "findResult" ? globHighlight(d.pattern) : undefined,
|
|
196
216
|
}),
|
|
197
217
|
);
|
|
198
218
|
return text;
|