pi-hashline-edit-pro 6.1.2 → 6.3.0
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/README.md +41 -23
- package/index.ts +20 -12
- package/package.json +3 -2
- package/src/anchor-registry.ts +22 -5
- package/src/auto-read-all.ts +39 -21
- package/src/batch.ts +127 -41
- package/src/commit.ts +1 -1
- package/src/config-ui.ts +135 -55
- package/src/config.ts +27 -9
- package/src/copy-move.ts +61 -72
- package/src/edit-common.ts +6 -4
- package/src/grep.ts +53 -121
- package/src/hash-store.ts +13 -12
- package/src/hashline/resolve.ts +13 -2
- package/src/insert.ts +40 -34
- package/src/line-endings.ts +10 -1
- package/src/outline/index.ts +46 -0
- package/src/outline/parsers.ts +42 -0
- package/src/outline/render.ts +170 -0
- package/src/outline/vendor/LICENSE +201 -0
- package/src/outline/vendor/README.md +17 -0
- package/src/outline/vendor/parsers/blade.ts +138 -0
- package/src/outline/vendor/parsers/index.ts +75 -0
- package/src/outline/vendor/parsers/java.ts +501 -0
- package/src/outline/vendor/parsers/manager.ts +1721 -0
- package/src/outline/vendor/parsers/php.ts +332 -0
- package/src/outline/vendor/parsers/sass.ts +416 -0
- package/src/outline/vendor/parsers/scss.ts +566 -0
- package/src/outline/vendor/types.ts +82 -0
- package/src/outline/vendor/wasm/LICENSES.md +37 -0
- package/src/outline/vendor/wasm/tree-sitter-blade.wasm +0 -0
- package/src/outline/vendor/wasm/tree-sitter-go.wasm +0 -0
- package/src/outline/vendor/wasm/tree-sitter-java.wasm +0 -0
- package/src/outline/vendor/wasm/tree-sitter-javascript.wasm +0 -0
- package/src/outline/vendor/wasm/tree-sitter-php.wasm +0 -0
- package/src/outline/vendor/wasm/tree-sitter-python.wasm +0 -0
- package/src/outline/vendor/wasm/tree-sitter-rust.wasm +0 -0
- package/src/outline/vendor/wasm/tree-sitter-sass.wasm +0 -0
- package/src/outline/vendor/wasm/tree-sitter-scss.wasm +0 -0
- package/src/outline/vendor/wasm/tree-sitter-solidity.wasm +0 -0
- package/src/outline/vendor/wasm/tree-sitter-tsx.wasm +0 -0
- package/src/outline/vendor/wasm/tree-sitter-typescript.wasm +0 -0
- package/src/read.ts +118 -41
- package/src/replace-match.ts +42 -41
- package/src/replace-undo.ts +4 -4
- package/src/replace.ts +30 -19
- package/tool-prompts/read-guidelines.md +3 -0
- package/{prompts → tool-prompts}/read.md +2 -0
- package/prompts/read-guidelines.md +0 -1
- package/src/auto-read-all-state.ts +0 -27
- /package/{prompts → tool-prompts}/copy-guidelines.md +0 -0
- /package/{prompts → tool-prompts}/copy-snippet.md +0 -0
- /package/{prompts → tool-prompts}/copy.md +0 -0
- /package/{prompts → tool-prompts}/grep-guidelines.md +0 -0
- /package/{prompts → tool-prompts}/grep-snippet.md +0 -0
- /package/{prompts → tool-prompts}/grep.md +0 -0
- /package/{prompts → tool-prompts}/insert-guidelines.md +0 -0
- /package/{prompts → tool-prompts}/insert-snippet.md +0 -0
- /package/{prompts → tool-prompts}/insert.md +0 -0
- /package/{prompts → tool-prompts}/move-guidelines.md +0 -0
- /package/{prompts → tool-prompts}/move-snippet.md +0 -0
- /package/{prompts → tool-prompts}/move.md +0 -0
- /package/{prompts → tool-prompts}/read-snippet.md +0 -0
- /package/{prompts → tool-prompts}/replace-guidelines.md +0 -0
- /package/{prompts → tool-prompts}/replace-match-guidelines.md +0 -0
- /package/{prompts → tool-prompts}/replace-match-snippet.md +0 -0
- /package/{prompts → tool-prompts}/replace-match.md +0 -0
- /package/{prompts → tool-prompts}/replace-snippet.md +0 -0
- /package/{prompts → tool-prompts}/replace.md +0 -0
- /package/{prompts → tool-prompts}/transfer-guidelines.md +0 -0
- /package/{prompts → tool-prompts}/undo-last-change-guidelines.md +0 -0
- /package/{prompts → tool-prompts}/undo-last-change-snippet.md +0 -0
- /package/{prompts → tool-prompts}/undo-last-change.md +0 -0
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# Vendored tree-sitter parsers
|
|
2
|
+
|
|
3
|
+
These files are copied from [pi-codebase-reader](https://github.com/HanzCEO/pi-codebase-reader) 0.8.0, licensed under Apache-2.0 (see `LICENSE`), with these modifications:
|
|
4
|
+
|
|
5
|
+
- `parsers/manager.ts` loads every grammar from `../wasm/` instead of resolving WASM files from npm grammar packages.
|
|
6
|
+
- `parsers/manager.ts` no longer supports Smali: the published `tree-sitter-smali` package ships no WASM, so `parsers/smali.ts` is not vendored.
|
|
7
|
+
- `parsers/manager.ts` recurses into the bodies of exported classes, interfaces, and enums. Upstream looked for the body on the `export_statement` node, which dropped every member of an exported declaration.
|
|
8
|
+
- `parsers/manager.ts` extracts Markdown with the upstream regex parser instead of a tree-sitter grammar; the `markdown` entry in its grammar registry supplies only the display label, and no Markdown WASM is shipped.
|
|
9
|
+
- `parsers/manager.ts` extracts anonymous default exports (`export default function`/`class`), function expressions, top-level variable declarators, and nested `describe`/`test`/`it` calls, so entry-point, config, data, and test files outline instead of falling back to a preview.
|
|
10
|
+
- `parsers/manager.ts` adds PHP support: `parsers/php.ts` outlines classes, interfaces, traits, enums, methods, properties, constants, top-level functions, and assigned closures.
|
|
11
|
+
- `parsers/manager.ts` adds Blade support: `parsers/blade.ts` outlines `@section`/`@push`/`@once`/`@php` blocks, named directives such as `@extends` and `@include`, and `<x-…>`/`<livewire:…>` components.
|
|
12
|
+
|
|
13
|
+
The `wasm/` grammars come from the MIT-licensed `tree-sitter-javascript`, `tree-sitter-typescript`, `tree-sitter-python`, `tree-sitter-go`, `tree-sitter-rust`, `tree-sitter-solidity`, and `tree-sitter-java` packages, plus pi-codebase-reader's vendored Sass and SCSS grammars, the VS Code WebAssembly build of `tree-sitter-php`, and `tree-sitter-blade` built from v0.12.3; see `wasm/LICENSES.md` for the per-grammar copyright notices. Comments in these files are upstream prose and are exempt from this repository's no-comments rule.
|
|
14
|
+
|
|
15
|
+
## Rebuilding the Blade grammar
|
|
16
|
+
|
|
17
|
+
`tree-sitter-blade.wasm` is built from tag `v0.12.3` of [tree-sitter-blade](https://github.com/EmranMR/tree-sitter-blade) with `tree-sitter-cli` 0.25.6. Run `npx -y tree-sitter-cli@0.25.6 build --wasm` in the grammar checkout with `emcc` on `PATH`, or dispatch the `build-blade-wasm` workflow and copy its artifact into `wasm/`.
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import type { Node } from "web-tree-sitter";
|
|
2
|
+
import type { SymbolInfo } from "../types.js";
|
|
3
|
+
|
|
4
|
+
const BLOCK_LABELS: Record<string, string> = {
|
|
5
|
+
section: "section",
|
|
6
|
+
stack: "stack",
|
|
7
|
+
once: "once",
|
|
8
|
+
php_statement: "php",
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
const SKIPPED_TYPES = new Set([
|
|
12
|
+
"attribute",
|
|
13
|
+
"quoted_attribute_value",
|
|
14
|
+
"attribute_value",
|
|
15
|
+
"start_tag",
|
|
16
|
+
"end_tag",
|
|
17
|
+
"self_closing_tag",
|
|
18
|
+
"erroneous_end_tag",
|
|
19
|
+
"parameter",
|
|
20
|
+
]);
|
|
21
|
+
|
|
22
|
+
function textOf(node: Node, source: string): string {
|
|
23
|
+
return source.slice(node.startIndex, node.endIndex);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function rangeOf(node: Node): [number, number] {
|
|
27
|
+
return [node.startPosition.row + 1, node.endPosition.row + 1];
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function findChild(node: Node, type: string): Node | undefined {
|
|
31
|
+
for (let i = 0; i < node.namedChildCount; i++) {
|
|
32
|
+
const child = node.namedChild(i);
|
|
33
|
+
if (child !== null && child.type === type) return child;
|
|
34
|
+
}
|
|
35
|
+
return undefined;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function unquote(text: string): string | undefined {
|
|
39
|
+
const trimmed = text.trim();
|
|
40
|
+
const quoted =
|
|
41
|
+
trimmed.length >= 2 &&
|
|
42
|
+
((trimmed.startsWith("'") && trimmed.endsWith("'")) || (trimmed.startsWith('"') && trimmed.endsWith('"')));
|
|
43
|
+
return quoted ? trimmed.slice(1, -1) : undefined;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function parameterName(node: Node, source: string): string {
|
|
47
|
+
const parameter = findChild(node, "parameter");
|
|
48
|
+
if (parameter === undefined) return "";
|
|
49
|
+
const raw = textOf(parameter, source);
|
|
50
|
+
return unquote(raw) ?? raw.trim();
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function bodyStart(node: Node): number {
|
|
54
|
+
const children = node.namedChildren;
|
|
55
|
+
let index = 0;
|
|
56
|
+
const first = children[index];
|
|
57
|
+
if (first !== null && first !== undefined && (first.type === "directive" || first.type === "directive_start")) {
|
|
58
|
+
index += 1;
|
|
59
|
+
}
|
|
60
|
+
while (index < children.length && children[index]?.type === "parameter") index += 1;
|
|
61
|
+
return index;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function blockSymbol(node: Node, source: string): SymbolInfo | null {
|
|
65
|
+
const label = BLOCK_LABELS[node.type];
|
|
66
|
+
if (label === undefined) return null;
|
|
67
|
+
const [startLine, endLine] = rangeOf(node);
|
|
68
|
+
return { name: parameterName(node, source), type: label, startLine, endLine };
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function directiveSymbol(node: Node, parameter: Node | null, source: string): SymbolInfo | null {
|
|
72
|
+
if (parameter === null || parameter.type !== "parameter") return null;
|
|
73
|
+
const label = textOf(node, source).replace(/^@/, "");
|
|
74
|
+
if (label.length === 0) return null;
|
|
75
|
+
const [startLine, endLine] = rangeOf(node);
|
|
76
|
+
const raw = textOf(parameter, source);
|
|
77
|
+
const quoted = unquote(raw);
|
|
78
|
+
return quoted === undefined
|
|
79
|
+
? { name: "", type: label, startLine, endLine, detail: raw.trim() }
|
|
80
|
+
: { name: quoted, type: label, startLine, endLine };
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function componentSymbol(node: Node, source: string): SymbolInfo | null {
|
|
84
|
+
const tag = findChild(node, "start_tag") ?? findChild(node, "self_closing_tag");
|
|
85
|
+
if (tag === undefined) return null;
|
|
86
|
+
const tagName = findChild(tag, "tag_name");
|
|
87
|
+
if (tagName === undefined) return null;
|
|
88
|
+
const name = textOf(tagName, source);
|
|
89
|
+
const [startLine, endLine] = rangeOf(node);
|
|
90
|
+
if (name.startsWith("x-")) return { name, type: "component", startLine, endLine };
|
|
91
|
+
if (name.startsWith("livewire:")) {
|
|
92
|
+
return { name: name.slice("livewire:".length), type: "livewire", startLine, endLine };
|
|
93
|
+
}
|
|
94
|
+
return null;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function collect(node: Node, source: string, depth: number, maxDepth: number, sink: SymbolInfo[], start = 0): void {
|
|
98
|
+
if (SKIPPED_TYPES.has(node.type)) return;
|
|
99
|
+
const children = node.namedChildren;
|
|
100
|
+
for (let i = start; i < children.length; i++) {
|
|
101
|
+
const child = children[i];
|
|
102
|
+
if (child === null || child === undefined) continue;
|
|
103
|
+
const block = blockSymbol(child, source);
|
|
104
|
+
if (block !== null) {
|
|
105
|
+
sink.push(block);
|
|
106
|
+
if (depth < maxDepth) {
|
|
107
|
+
const nested: SymbolInfo[] = [];
|
|
108
|
+
collect(child, source, depth + 1, maxDepth, nested, bodyStart(child));
|
|
109
|
+
if (nested.length > 0) block.children = nested;
|
|
110
|
+
}
|
|
111
|
+
continue;
|
|
112
|
+
}
|
|
113
|
+
if (child.type === "directive") {
|
|
114
|
+
const symbol = directiveSymbol(child, children[i + 1] ?? null, source);
|
|
115
|
+
if (symbol !== null) sink.push(symbol);
|
|
116
|
+
continue;
|
|
117
|
+
}
|
|
118
|
+
if (child.type === "element") {
|
|
119
|
+
const symbol = componentSymbol(child, source);
|
|
120
|
+
if (symbol !== null) {
|
|
121
|
+
sink.push(symbol);
|
|
122
|
+
if (depth < maxDepth) {
|
|
123
|
+
const nested: SymbolInfo[] = [];
|
|
124
|
+
collect(child, source, depth + 1, maxDepth, nested);
|
|
125
|
+
if (nested.length > 0) symbol.children = nested;
|
|
126
|
+
}
|
|
127
|
+
continue;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
collect(child, source, depth, maxDepth, sink);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
export function extractBlade(node: Node, source: string, depth: number): SymbolInfo[] {
|
|
135
|
+
const results: SymbolInfo[] = [];
|
|
136
|
+
collect(node, source, depth, depth + 10, results);
|
|
137
|
+
return results;
|
|
138
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Parser dispatcher.
|
|
3
|
+
*
|
|
4
|
+
* Maps file extensions to the appropriate parser, maintaining a shared
|
|
5
|
+
* tree-sitter parser manager for WASM-based grammars.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import type { SymbolInfo } from "../types.js";
|
|
9
|
+
import {
|
|
10
|
+
detectLanguage,
|
|
11
|
+
languageLabel,
|
|
12
|
+
parseCode,
|
|
13
|
+
parseFileImports,
|
|
14
|
+
type ImportInfo,
|
|
15
|
+
} from "./manager.js";
|
|
16
|
+
|
|
17
|
+
export { detectLanguage, languageLabel, parseFileImports };
|
|
18
|
+
export type { ImportInfo };
|
|
19
|
+
|
|
20
|
+
export interface ParseFileResult {
|
|
21
|
+
symbols: SymbolInfo[];
|
|
22
|
+
language: string | null;
|
|
23
|
+
languageName: string;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Parse a source file's content and extract structural symbols.
|
|
28
|
+
* Returns empty symbols array if language is unsupported or parsing fails.
|
|
29
|
+
*/
|
|
30
|
+
export async function parseSourceFile(
|
|
31
|
+
filePath: string,
|
|
32
|
+
content: string,
|
|
33
|
+
): Promise<ParseFileResult> {
|
|
34
|
+
const lang = detectLanguage(filePath);
|
|
35
|
+
|
|
36
|
+
if (!lang) {
|
|
37
|
+
return { symbols: [], language: null, languageName: "Unknown" };
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
try {
|
|
41
|
+
const symbols = await parseCode(lang, content);
|
|
42
|
+
return { symbols, language: lang, languageName: languageLabel(lang) };
|
|
43
|
+
} catch (err) {
|
|
44
|
+
console.warn(
|
|
45
|
+
`[codebase-reader] Parse failed for ${filePath}:`,
|
|
46
|
+
err instanceof Error ? err.message : String(err),
|
|
47
|
+
);
|
|
48
|
+
return { symbols: [], language: lang, languageName: languageLabel(lang) };
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Extract import statements from a source file.
|
|
54
|
+
* Returns empty array if language is unsupported or parsing fails.
|
|
55
|
+
*/
|
|
56
|
+
export async function extractFileImports(
|
|
57
|
+
filePath: string,
|
|
58
|
+
content: string,
|
|
59
|
+
): Promise<ImportInfo[]> {
|
|
60
|
+
const lang = detectLanguage(filePath);
|
|
61
|
+
|
|
62
|
+
if (!lang) {
|
|
63
|
+
return [];
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
try {
|
|
67
|
+
return await parseFileImports(lang, content);
|
|
68
|
+
} catch (err) {
|
|
69
|
+
console.warn(
|
|
70
|
+
`[codebase-reader] Import parse failed for ${filePath}:`,
|
|
71
|
+
err instanceof Error ? err.message : String(err),
|
|
72
|
+
);
|
|
73
|
+
return [];
|
|
74
|
+
}
|
|
75
|
+
}
|