@akiojin/unity-mcp-server 2.40.2 → 2.40.3
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 +21 -0
- package/bin/unity-mcp-server +1 -1
- package/package.json +1 -1
- package/src/core/codeIndex.js +64 -15
- package/src/core/server.js +3 -34
- package/src/handlers/analysis/AnalyzeSceneContentsToolHandler.js +27 -24
- package/src/handlers/analysis/FindByComponentToolHandler.js +4 -1
- package/src/handlers/analysis/GetAnimatorStateToolHandler.js +5 -5
- package/src/handlers/analysis/GetComponentValuesToolHandler.js +4 -1
- package/src/handlers/analysis/GetGameObjectDetailsToolHandler.js +27 -24
- package/src/handlers/analysis/GetInputActionsStateToolHandler.js +5 -5
- package/src/handlers/analysis/GetObjectReferencesToolHandler.js +4 -1
- package/src/handlers/asset/AssetDatabaseManageToolHandler.js +24 -6
- package/src/handlers/asset/AssetDependencyAnalyzeToolHandler.js +21 -11
- package/src/handlers/asset/AssetImportSettingsManageToolHandler.js +7 -7
- package/src/handlers/asset/AssetMaterialCreateToolHandler.js +78 -81
- package/src/handlers/asset/AssetMaterialModifyToolHandler.js +57 -61
- package/src/handlers/asset/AssetPrefabCreateToolHandler.js +61 -64
- package/src/handlers/asset/AssetPrefabExitModeToolHandler.js +9 -13
- package/src/handlers/asset/AssetPrefabInstantiateToolHandler.js +110 -116
- package/src/handlers/asset/AssetPrefabModifyToolHandler.js +58 -58
- package/src/handlers/asset/AssetPrefabOpenToolHandler.js +7 -5
- package/src/handlers/asset/AssetPrefabSaveToolHandler.js +13 -6
- package/src/handlers/compilation/CompilationGetStateToolHandler.js +4 -3
- package/src/handlers/component/ComponentAddToolHandler.js +2 -2
- package/src/handlers/component/ComponentGetTypesToolHandler.js +17 -21
- package/src/handlers/component/ComponentListToolHandler.js +5 -3
- package/src/handlers/component/ComponentModifyToolHandler.js +3 -3
- package/src/handlers/component/ComponentRemoveToolHandler.js +2 -2
- package/src/handlers/console/ConsoleClearToolHandler.js +36 -46
- package/src/handlers/editor/EditorLayersManageToolHandler.js +7 -6
- package/src/handlers/editor/EditorTagsManageToolHandler.js +20 -11
- package/src/handlers/editor/EditorToolsManageToolHandler.js +2 -2
- package/src/handlers/editor/EditorWindowsManageToolHandler.js +6 -5
- package/src/handlers/gameobject/GameObjectCreateToolHandler.js +62 -66
- package/src/handlers/gameobject/GameObjectDeleteToolHandler.js +9 -9
- package/src/handlers/gameobject/GameObjectFindToolHandler.js +13 -11
- package/src/handlers/gameobject/GameObjectGetHierarchyToolHandler.js +22 -16
- package/src/handlers/input/InputActionAddToolHandler.js +2 -2
- package/src/handlers/input/InputActionMapCreateToolHandler.js +2 -2
- package/src/handlers/input/InputActionMapRemoveToolHandler.js +2 -2
- package/src/handlers/input/InputActionRemoveToolHandler.js +2 -2
- package/src/handlers/input/InputBindingAddToolHandler.js +2 -2
- package/src/handlers/input/InputBindingCompositeCreateToolHandler.js +2 -2
- package/src/handlers/input/InputBindingRemoveAllToolHandler.js +2 -2
- package/src/handlers/input/InputBindingRemoveToolHandler.js +2 -2
- package/src/handlers/input/InputControlSchemesManageToolHandler.js +2 -2
- package/src/handlers/package/PackageManagerToolHandler.js +41 -44
- package/src/handlers/package/RegistryConfigToolHandler.js +28 -7
- package/src/handlers/playmode/PlaymodeGetStateToolHandler.js +12 -16
- package/src/handlers/playmode/PlaymodePauseToolHandler.js +8 -12
- package/src/handlers/playmode/PlaymodeWaitForStateToolHandler.js +6 -3
- package/src/handlers/scene/GetSceneInfoToolHandler.js +11 -11
- package/src/handlers/scene/SceneCreateToolHandler.js +28 -31
- package/src/handlers/scene/SceneListToolHandler.js +21 -24
- package/src/handlers/scene/SceneLoadToolHandler.js +27 -29
- package/src/handlers/scene/SceneSaveToolHandler.js +19 -22
- package/src/handlers/screenshot/ScreenshotCaptureToolHandler.js +88 -66
- package/src/handlers/script/CodeIndexStatusToolHandler.js +4 -3
- package/src/handlers/script/CodeIndexUpdateToolHandler.js +24 -14
- package/src/handlers/script/ScriptCreateClassToolHandler.js +44 -9
- package/src/handlers/script/ScriptPackagesListToolHandler.js +91 -91
- package/src/handlers/script/ScriptRefactorRenameToolHandler.js +80 -71
- package/src/handlers/script/ScriptRemoveSymbolToolHandler.js +21 -7
- package/src/handlers/script/ScriptSearchToolHandler.js +299 -266
- package/src/handlers/script/ScriptSymbolsGetToolHandler.js +88 -79
- package/src/handlers/settings/SettingsGetToolHandler.js +28 -13
- package/src/handlers/settings/SettingsUpdateToolHandler.js +20 -6
- package/src/handlers/ui/UIClickElementToolHandler.js +87 -96
- package/src/handlers/ui/UIFindElementsToolHandler.js +45 -55
- package/src/handlers/ui/UIGetElementStateToolHandler.js +35 -43
- package/src/handlers/ui/UISetElementValueToolHandler.js +42 -49
- package/src/handlers/ui/UISimulateInputToolHandler.js +134 -136
- package/src/handlers/video/VideoCaptureForToolHandler.js +24 -7
- package/src/lsp/LspRpcClient.js +24 -12
- package/src/tools/analysis/analyzeSceneContents.js +85 -85
- package/src/tools/analysis/findByComponent.js +73 -73
- package/src/tools/analysis/getAnimatorState.js +287 -287
- package/src/tools/analysis/getComponentValues.js +161 -161
- package/src/tools/analysis/getGameObjectDetails.js +138 -138
- package/src/tools/analysis/getInputActionsState.js +291 -291
- package/src/tools/analysis/getObjectReferences.js +72 -72
- package/src/tools/input/inputActionsEditor.js +522 -474
- package/src/tools/scene/createScene.js +98 -97
- package/src/tools/scene/getSceneInfo.js +82 -81
- package/src/tools/scene/listScenes.js +70 -69
- package/src/tools/scene/loadScene.js +108 -106
- package/src/tools/scene/saveScene.js +78 -77
- package/src/tools/system/ping.js +9 -12
- package/src/utils/validators.js +2 -2
|
@@ -5,316 +5,349 @@ import { ProjectInfoProvider } from '../../core/projectInfo.js';
|
|
|
5
5
|
import { logger, config } from '../../core/config.js';
|
|
6
6
|
|
|
7
7
|
export class ScriptSearchToolHandler extends BaseToolHandler {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
8
|
+
constructor(unityConnection) {
|
|
9
|
+
super(
|
|
10
|
+
'script_search',
|
|
11
|
+
'Search C# by substring/regex/glob with pagination and snippet context. PRIORITY: Use to locate symbols/files; avoid full contents. Use returnMode="snippets" (or "metadata") with small snippetContext (1–2). Narrow aggressively via include globs under Assets/** or Packages/** and semantic filters (namespace/container/identifier). Do NOT prefix repository folders.',
|
|
12
|
+
{
|
|
13
|
+
type: 'object',
|
|
14
|
+
properties: {
|
|
15
|
+
pattern: {
|
|
16
|
+
type: 'string',
|
|
17
|
+
description:
|
|
18
|
+
'Pattern to search (required unless patternType="glob"). For glob mode, use include/exclude.'
|
|
19
|
+
},
|
|
20
|
+
patternType: {
|
|
21
|
+
type: 'string',
|
|
22
|
+
enum: ['substring', 'regex', 'glob'],
|
|
23
|
+
description: 'Pattern matching strategy: substring (default), regex, or glob-only scan.'
|
|
24
|
+
},
|
|
25
|
+
flags: {
|
|
26
|
+
type: 'array',
|
|
27
|
+
items: { type: 'string' },
|
|
28
|
+
description: 'Regex flags (e.g., ["i","m","s","u"]). Ignored for substring/glob.'
|
|
29
|
+
},
|
|
30
|
+
scope: {
|
|
31
|
+
type: 'string',
|
|
32
|
+
enum: ['assets', 'packages', 'embedded', 'all'],
|
|
33
|
+
description:
|
|
34
|
+
'Search scope: assets (Assets/, default), packages (Packages/), embedded, or all.'
|
|
35
|
+
},
|
|
36
|
+
include: {
|
|
37
|
+
type: 'string',
|
|
38
|
+
description:
|
|
39
|
+
'Include glob pattern (project-relative, default: **/*.cs). Examples: Assets/**/*.cs or Packages/unity-mcp-server/**/*.cs.'
|
|
40
|
+
},
|
|
41
|
+
exclude: {
|
|
42
|
+
type: 'string',
|
|
43
|
+
description: 'Exclude glob pattern (e.g., **/Tests/**).'
|
|
44
|
+
},
|
|
45
|
+
pageSize: {
|
|
46
|
+
type: 'number',
|
|
47
|
+
description: 'Maximum results per page for pagination.'
|
|
48
|
+
},
|
|
49
|
+
maxMatchesPerFile: {
|
|
50
|
+
type: 'number',
|
|
51
|
+
description: 'Cap matches returned per file.'
|
|
52
|
+
},
|
|
53
|
+
snippetContext: {
|
|
54
|
+
type: 'number',
|
|
55
|
+
description: 'Number of context lines around each match.'
|
|
56
|
+
},
|
|
57
|
+
maxBytes: {
|
|
58
|
+
type: 'number',
|
|
59
|
+
description: 'Maximum response size (bytes) to keep outputs LLM‑friendly.'
|
|
60
|
+
},
|
|
61
|
+
returnMode: {
|
|
62
|
+
type: 'string',
|
|
63
|
+
enum: ['metadata', 'snippets', 'full'],
|
|
64
|
+
description: 'Result detail: metadata (fast), snippets (recommended), or full.'
|
|
65
|
+
},
|
|
66
|
+
detail: {
|
|
67
|
+
type: 'string',
|
|
68
|
+
enum: ['compact', 'metadata', 'snippets', 'full'],
|
|
69
|
+
description: 'Alias of returnMode. `compact` maps to `snippets`.'
|
|
70
|
+
},
|
|
71
|
+
startAfter: {
|
|
72
|
+
type: 'string',
|
|
73
|
+
description: 'Opaque cursor for pagination (use value from previous page).'
|
|
74
|
+
},
|
|
75
|
+
maxFileSizeKB: {
|
|
76
|
+
type: 'number',
|
|
77
|
+
description: 'Skip files larger than this (KB).'
|
|
78
|
+
},
|
|
79
|
+
codeOnly: {
|
|
80
|
+
type: 'boolean',
|
|
81
|
+
description: 'If true, exclude comments/whitespace to reduce noise (default: true).'
|
|
82
|
+
},
|
|
83
|
+
container: {
|
|
84
|
+
type: 'string',
|
|
85
|
+
description: 'Semantic filter: container (e.g., class name).'
|
|
86
|
+
},
|
|
87
|
+
namespace: {
|
|
88
|
+
type: 'string',
|
|
89
|
+
description: 'Semantic filter: namespace.'
|
|
90
|
+
},
|
|
91
|
+
identifier: {
|
|
92
|
+
type: 'string',
|
|
93
|
+
description: 'Semantic filter: identifier (e.g., method or field name).'
|
|
94
|
+
}
|
|
95
|
+
},
|
|
96
|
+
required: []
|
|
97
|
+
}
|
|
98
|
+
);
|
|
99
|
+
this.unityConnection = unityConnection;
|
|
100
|
+
this.projectInfo = new ProjectInfoProvider(unityConnection);
|
|
101
|
+
this.configDefaultDetail = (config?.search?.defaultDetail || 'compact').toLowerCase();
|
|
102
|
+
this.configSearchEngine = (config?.search?.engine || 'naive').toLowerCase();
|
|
103
|
+
}
|
|
101
104
|
|
|
102
|
-
|
|
103
|
-
|
|
105
|
+
validate(params) {
|
|
106
|
+
super.validate(params);
|
|
104
107
|
|
|
105
|
-
|
|
108
|
+
const { pattern, patternType } = params;
|
|
106
109
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
}
|
|
110
|
+
// Pattern is required for non-glob pattern types
|
|
111
|
+
if (patternType !== 'glob' && !pattern) {
|
|
112
|
+
throw new Error('pattern is required for substring and regex search');
|
|
111
113
|
}
|
|
114
|
+
}
|
|
112
115
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
116
|
+
async execute(params) {
|
|
117
|
+
try {
|
|
118
|
+
const info = await this.projectInfo.get();
|
|
119
|
+
const {
|
|
120
|
+
pattern,
|
|
121
|
+
patternType = 'substring',
|
|
122
|
+
flags = [],
|
|
123
|
+
scope = 'assets',
|
|
124
|
+
include = '**/*.cs',
|
|
125
|
+
exclude,
|
|
126
|
+
pageSize = 20,
|
|
127
|
+
maxMatchesPerFile = 5,
|
|
128
|
+
snippetContext = 2,
|
|
129
|
+
maxBytes = 1024 * 64,
|
|
130
|
+
returnMode,
|
|
131
|
+
detail,
|
|
132
|
+
startAfter,
|
|
133
|
+
maxFileSizeKB = 1024,
|
|
134
|
+
codeOnly = true
|
|
135
|
+
} = params;
|
|
133
136
|
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
137
|
+
// Resolve detail/returnMode default and mapping
|
|
138
|
+
let effectiveDetail = (detail || '').toLowerCase();
|
|
139
|
+
if (!effectiveDetail && !returnMode) {
|
|
140
|
+
effectiveDetail = this.configDefaultDetail || 'compact';
|
|
141
|
+
}
|
|
142
|
+
const normalizedReturnMode = (() => {
|
|
143
|
+
const d = (effectiveDetail || '').toLowerCase();
|
|
144
|
+
if (d === 'compact') return 'snippets';
|
|
145
|
+
if (d === 'metadata' || d === 'snippets' || d === 'full') return d;
|
|
146
|
+
return returnMode || 'snippets';
|
|
147
|
+
})();
|
|
145
148
|
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
149
|
+
// Resolve search roots
|
|
150
|
+
const roots = [];
|
|
151
|
+
if (scope === 'assets' || scope === 'all') roots.push(info.assetsPath);
|
|
152
|
+
if (scope === 'packages' || scope === 'embedded' || scope === 'all')
|
|
153
|
+
roots.push(info.packagesPath);
|
|
150
154
|
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
155
|
+
const includeRx = globToRegExp(include);
|
|
156
|
+
const excludeRx = exclude ? globToRegExp(exclude) : null;
|
|
157
|
+
// Engine selection (future: treesitter). Currently fallback to naive.
|
|
158
|
+
if (this.configSearchEngine === 'treesitter') {
|
|
159
|
+
logger.debug('[script_search] tree-sitter engine requested; falling back to naive matcher');
|
|
160
|
+
}
|
|
161
|
+
const matcher = buildMatcher(patternType, pattern, flags);
|
|
158
162
|
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
163
|
+
const results = [];
|
|
164
|
+
const pathTable = [];
|
|
165
|
+
const pathId = new Map();
|
|
166
|
+
let bytes = 0;
|
|
167
|
+
let afterFound = !startAfter;
|
|
164
168
|
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
169
|
+
for await (const file of walk(roots)) {
|
|
170
|
+
// Pagination cursor: skip until we see startAfter
|
|
171
|
+
const rel = toRel(file, info.projectRoot);
|
|
172
|
+
if (!afterFound) {
|
|
173
|
+
if (rel === startAfter) afterFound = true;
|
|
174
|
+
else continue;
|
|
175
|
+
}
|
|
176
|
+
// Filters
|
|
177
|
+
if (!includeRx.test(rel)) continue;
|
|
178
|
+
if (excludeRx && excludeRx.test(rel)) continue;
|
|
179
|
+
if (!rel.toLowerCase().endsWith('.cs')) continue;
|
|
176
180
|
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
181
|
+
// Size guard
|
|
182
|
+
const st = await fs.stat(file).catch(() => null);
|
|
183
|
+
if (!st || st.size > maxFileSizeKB * 1024) continue;
|
|
180
184
|
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
+
// Read content
|
|
186
|
+
const text = await fs.readFile(file, 'utf8');
|
|
187
|
+
const lines = text.split('\n');
|
|
188
|
+
const filtered = codeOnly ? stripComments(lines) : lines;
|
|
185
189
|
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
190
|
+
let matches = 0;
|
|
191
|
+
const matchedLines = [];
|
|
192
|
+
for (let i = 0; i < filtered.length; i++) {
|
|
193
|
+
if (matches >= maxMatchesPerFile) break;
|
|
194
|
+
const line = filtered[i];
|
|
195
|
+
if (matcher(line)) {
|
|
196
|
+
matches++;
|
|
197
|
+
matchedLines.push(i + 1);
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
if (matches === 0) continue;
|
|
197
201
|
|
|
198
|
-
|
|
199
|
-
|
|
202
|
+
const id = pathId.has(rel)
|
|
203
|
+
? pathId.get(rel)
|
|
204
|
+
: (pathTable.push(rel) - 1, pathTable.length - 1);
|
|
205
|
+
pathId.set(rel, id);
|
|
200
206
|
|
|
201
|
-
|
|
202
|
-
|
|
207
|
+
const lineRanges = toRanges(matchedLines);
|
|
208
|
+
const item = { fileId: id, lineRanges };
|
|
203
209
|
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
210
|
+
if (normalizedReturnMode === 'snippets') {
|
|
211
|
+
// Build minimal snippets around first few matches
|
|
212
|
+
const snippets = [];
|
|
213
|
+
for (const ln of matchedLines.slice(0, maxMatchesPerFile)) {
|
|
214
|
+
const s = Math.max(1, ln - snippetContext);
|
|
215
|
+
const e = Math.min(lines.length, ln + snippetContext);
|
|
216
|
+
snippets.push({ line: ln, snippet: lines.slice(s - 1, e).join('\n') });
|
|
217
|
+
}
|
|
218
|
+
item.snippets = snippets;
|
|
219
|
+
}
|
|
214
220
|
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
221
|
+
const json = JSON.stringify(item);
|
|
222
|
+
bytes += Buffer.byteLength(json, 'utf8');
|
|
223
|
+
results.push(item);
|
|
218
224
|
|
|
219
|
-
|
|
220
|
-
|
|
225
|
+
if (results.length >= pageSize || bytes >= maxBytes) break;
|
|
226
|
+
}
|
|
221
227
|
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
228
|
+
return {
|
|
229
|
+
success: true,
|
|
230
|
+
total: results.length,
|
|
231
|
+
pathTable,
|
|
232
|
+
results,
|
|
233
|
+
cursor:
|
|
234
|
+
results.length && results.length >= pageSize ? pathTable[pathTable.length - 1] : null
|
|
235
|
+
};
|
|
236
|
+
} catch (e) {
|
|
237
|
+
logger.error(`[script_search] failed: ${e.message}`);
|
|
238
|
+
return { error: e.message };
|
|
233
239
|
}
|
|
240
|
+
}
|
|
234
241
|
}
|
|
235
242
|
|
|
236
243
|
// --- helpers ---
|
|
237
244
|
function globToRegExp(glob) {
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
+
// Very small subset: **/* and * and ? handling
|
|
246
|
+
const esc = glob
|
|
247
|
+
.replace(/[.+^${}()|[\]\\]/g, '\\$&')
|
|
248
|
+
.replace(/\*\*/g, '§§')
|
|
249
|
+
.replace(/\*/g, '[^/]*')
|
|
250
|
+
.replace(/§§/g, '.*')
|
|
251
|
+
.replace(/\?/g, '.');
|
|
252
|
+
return new RegExp('^' + esc + '$');
|
|
245
253
|
}
|
|
246
254
|
|
|
247
255
|
function buildMatcher(type, pattern, flags) {
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
256
|
+
if (type === 'regex') {
|
|
257
|
+
const fl = Array.isArray(flags) ? flags.join('') : '';
|
|
258
|
+
const rx = new RegExp(pattern, fl);
|
|
259
|
+
return s => rx.test(s);
|
|
260
|
+
}
|
|
261
|
+
if (type === 'glob') {
|
|
262
|
+
// glob-only scan: no content matcher, treat every file as match
|
|
263
|
+
return () => true;
|
|
264
|
+
}
|
|
265
|
+
// substring
|
|
266
|
+
const p = pattern || '';
|
|
267
|
+
return s => p && s.includes(p);
|
|
260
268
|
}
|
|
261
269
|
|
|
262
270
|
async function* walk(roots) {
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
271
|
+
for (const r of roots) {
|
|
272
|
+
yield* walkDir(r);
|
|
273
|
+
}
|
|
266
274
|
}
|
|
267
275
|
|
|
268
276
|
async function* walkDir(dir) {
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
277
|
+
let entries;
|
|
278
|
+
try {
|
|
279
|
+
entries = await fs.readdir(dir, { withFileTypes: true });
|
|
280
|
+
} catch {
|
|
281
|
+
return;
|
|
282
|
+
}
|
|
283
|
+
for (const e of entries) {
|
|
284
|
+
const p = path.join(dir, e.name);
|
|
285
|
+
if (e.isDirectory()) {
|
|
286
|
+
yield* walkDir(p);
|
|
287
|
+
} else if (e.isFile()) {
|
|
288
|
+
yield p.replace(/\\/g, '/');
|
|
278
289
|
}
|
|
290
|
+
}
|
|
279
291
|
}
|
|
280
292
|
|
|
281
293
|
function toRel(abs, projectRoot) {
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
294
|
+
const n = abs.replace(/\\/g, '/');
|
|
295
|
+
const base = projectRoot.replace(/\\/g, '/');
|
|
296
|
+
return n.startsWith(base) ? n.substring(base.length + 1) : n;
|
|
285
297
|
}
|
|
286
298
|
|
|
287
299
|
function toRanges(lines) {
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
300
|
+
if (!lines.length) return '';
|
|
301
|
+
const out = [];
|
|
302
|
+
let s = lines[0],
|
|
303
|
+
prev = lines[0];
|
|
304
|
+
for (let i = 1; i < lines.length; i++) {
|
|
305
|
+
const v = lines[i];
|
|
306
|
+
if (v === prev + 1) {
|
|
307
|
+
prev = v;
|
|
308
|
+
continue;
|
|
296
309
|
}
|
|
297
310
|
out.push(s === prev ? `${s}` : `${s}-${prev}`);
|
|
298
|
-
|
|
311
|
+
s = prev = v;
|
|
312
|
+
}
|
|
313
|
+
out.push(s === prev ? `${s}` : `${s}-${prev}`);
|
|
314
|
+
return out.join(',');
|
|
299
315
|
}
|
|
300
316
|
|
|
301
317
|
function stripComments(lines) {
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
+
// naive removal of // line comments and /* */ blocks
|
|
319
|
+
const out = [];
|
|
320
|
+
let inBlock = false;
|
|
321
|
+
for (const line of lines) {
|
|
322
|
+
let s = line;
|
|
323
|
+
if (inBlock) {
|
|
324
|
+
const end = s.indexOf('*/');
|
|
325
|
+
if (end >= 0) {
|
|
326
|
+
s = s.slice(end + 2);
|
|
327
|
+
inBlock = false;
|
|
328
|
+
} else {
|
|
329
|
+
out.push('');
|
|
330
|
+
continue;
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
let i = 0;
|
|
334
|
+
let res = '';
|
|
335
|
+
while (i < s.length) {
|
|
336
|
+
if (s.startsWith('/*', i)) {
|
|
337
|
+
inBlock = true;
|
|
338
|
+
const end = s.indexOf('*/', i + 2);
|
|
339
|
+
if (end >= 0) {
|
|
340
|
+
i = end + 2;
|
|
341
|
+
inBlock = false;
|
|
342
|
+
continue;
|
|
343
|
+
} else break;
|
|
344
|
+
}
|
|
345
|
+
if (s.startsWith('//', i)) {
|
|
346
|
+
break;
|
|
347
|
+
}
|
|
348
|
+
res += s[i++];
|
|
318
349
|
}
|
|
319
|
-
|
|
350
|
+
out.push(res);
|
|
351
|
+
}
|
|
352
|
+
return out;
|
|
320
353
|
}
|