autosnippet 1.1.20 → 1.1.22
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/bin/asnip.js +21 -7
- package/bin/findPath.js +19 -2
- package/bin/injection.js +121 -31
- package/bin/install.js +11 -1
- package/bin/watch.js +7 -5
- package/package.json +1 -1
package/bin/asnip.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
const fs = require('fs');
|
|
4
|
+
const path = require('path');
|
|
4
5
|
// 读取输入命令
|
|
5
6
|
const inquirer = require('inquirer');
|
|
6
7
|
// 命令行工具
|
|
@@ -219,19 +220,32 @@ commander
|
|
|
219
220
|
commander
|
|
220
221
|
.command('w')
|
|
221
222
|
.description('recognize that Snippet automatically injects dependency header files')
|
|
222
|
-
.action(() => {
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
223
|
+
.action(async () => {
|
|
224
|
+
// ✅ 从执行位置向上查找 AutoSnippetRoot.boxspec.json,找到根目录
|
|
225
|
+
const projectRoot = await findPath.findProjectRoot(CMD_PATH);
|
|
226
|
+
|
|
227
|
+
if (!projectRoot) {
|
|
228
|
+
console.error('未找到项目根目录(AutoSnippetRoot.boxspec.json)。');
|
|
229
|
+
console.error('请先使用 asd root 命令在项目根目录创建根目录标记文件。');
|
|
230
|
+
return;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
console.log(`[asd w] 项目根目录: ${projectRoot}`);
|
|
234
|
+
|
|
235
|
+
// ✅ 使用根目录的 AutoSnippetRoot.boxspec.json 作为配置文件
|
|
236
|
+
const rootSpecFile = path.join(projectRoot, findPath.ROOT_MARKER_NAME);
|
|
237
|
+
console.log(`[asd w] 使用配置文件: ${rootSpecFile}`);
|
|
238
|
+
|
|
239
|
+
// 先安装 snippets
|
|
240
|
+
install.addCodeSnippets(rootSpecFile);
|
|
241
|
+
// 在根目录启动监听
|
|
242
|
+
watch.watchFileChange(rootSpecFile, projectRoot);
|
|
227
243
|
});
|
|
228
244
|
|
|
229
245
|
commander
|
|
230
246
|
.command('root')
|
|
231
247
|
.description('mark current directory as project root by creating AutoSnippetRoot.boxspec.json')
|
|
232
248
|
.action(() => {
|
|
233
|
-
const fs = require('fs');
|
|
234
|
-
const path = require('path');
|
|
235
249
|
const rootMarkerPath = path.join(CMD_PATH, 'AutoSnippetRoot.boxspec.json');
|
|
236
250
|
|
|
237
251
|
try {
|
package/bin/findPath.js
CHANGED
|
@@ -383,7 +383,24 @@ async function parsePackageSwift(packagePath) {
|
|
|
383
383
|
|
|
384
384
|
// 向下查找模块头文件(优化:适配 SPM 结构,优先查找 include/ModuleName/ 目录)
|
|
385
385
|
async function findSubHeaderPath(filePath, headerName, moduleName) {
|
|
386
|
-
// ✅ 优先查找
|
|
386
|
+
// ✅ 优先查找 Code/ 目录(实际源代码位置)
|
|
387
|
+
// 按照用户期望的顺序:target根目录 → Code → 子文件夹/头文件
|
|
388
|
+
const codePath = path.join(filePath, 'Code');
|
|
389
|
+
try {
|
|
390
|
+
const stats = await fs.promises.stat(codePath);
|
|
391
|
+
if (stats.isDirectory()) {
|
|
392
|
+
// 在 Code 目录中递归查找头文件
|
|
393
|
+
const result = await findSubHeaderPath(codePath, headerName, null);
|
|
394
|
+
if (result) {
|
|
395
|
+
return result;
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
} catch {
|
|
399
|
+
// Code 目录不存在,继续查找
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
// ✅ 降级:查找 include/ModuleName/ 目录(SPM 公共头文件结构)
|
|
403
|
+
// 注意:include/ 目录通常是符号链接,但作为后备方案保留
|
|
387
404
|
if (moduleName) {
|
|
388
405
|
const includePath = path.join(filePath, 'include', moduleName);
|
|
389
406
|
try {
|
|
@@ -402,7 +419,7 @@ async function findSubHeaderPath(filePath, headerName, moduleName) {
|
|
|
402
419
|
}
|
|
403
420
|
}
|
|
404
421
|
|
|
405
|
-
// ✅
|
|
422
|
+
// ✅ 最后:在整个模块目录中查找(深度限制)
|
|
406
423
|
try {
|
|
407
424
|
const entries = await getDirectoryEntries(filePath);
|
|
408
425
|
if (!entries) {
|
package/bin/injection.js
CHANGED
|
@@ -91,16 +91,40 @@ async function handleHeaderLine(specFile, updateFile, headerLine, importArray, i
|
|
|
91
91
|
// 获取当前文件所在的模块名(通过路径判断)
|
|
92
92
|
const currentModuleName = determineCurrentModuleName(updateFile, currentPackageInfo);
|
|
93
93
|
|
|
94
|
-
// ✅
|
|
95
|
-
|
|
96
|
-
|
|
94
|
+
// ✅ 优先从标记中获取相对路径,如果没有则从缓存获取
|
|
95
|
+
// 标记格式:// ahead <Module/Header.h> relative/path/to/Header.h
|
|
96
|
+
let headRelativePath = header.headRelativePathFromMark;
|
|
97
|
+
if (!headRelativePath) {
|
|
98
|
+
// 标记中没有相对路径,尝试从缓存获取
|
|
99
|
+
const headCache = await cache.getHeadCache(specFile);
|
|
100
|
+
if (headCache && headCache[header.headerName]) {
|
|
101
|
+
headRelativePath = headCache[header.headerName];
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
const headerInfo = await determineHeaderInfo(specFile, header, headRelativePath, currentPackageInfo, currentModuleName);
|
|
97
106
|
|
|
98
|
-
// ✅ 更新 header
|
|
99
|
-
header.moduleName =
|
|
100
|
-
header.
|
|
107
|
+
// ✅ 更新 header 中的模块名、相对路径和 specName
|
|
108
|
+
header.moduleName = headerInfo.moduleName;
|
|
109
|
+
header.headRelativePath = headerInfo.headRelativePath; // 相对于模块根目录的相对路径
|
|
110
|
+
header.specName = '<' + headerInfo.moduleName + '/' + header.headerName + '>';
|
|
101
111
|
|
|
102
112
|
// ✅ 判断是否为同一模块
|
|
103
|
-
const isSameModule = currentModuleName ===
|
|
113
|
+
const isSameModule = currentModuleName === headerInfo.moduleName;
|
|
114
|
+
|
|
115
|
+
// ✅ 如果是同一模块,计算相对于当前文件的相对路径
|
|
116
|
+
if (isSameModule && headerInfo.headRelativePath) {
|
|
117
|
+
// 当前文件相对于模块根目录的路径
|
|
118
|
+
const currentFileRelativeToModuleRoot = path.relative(currentPackageInfo.path, updateFile);
|
|
119
|
+
// 头文件相对于模块根目录的路径
|
|
120
|
+
const headRelativeToModuleRoot = headerInfo.headRelativePath;
|
|
121
|
+
|
|
122
|
+
// 计算从当前文件到头文件的相对路径
|
|
123
|
+
const currentFileDir = path.dirname(currentFileRelativeToModuleRoot);
|
|
124
|
+
const relativePathToHeader = path.relative(currentFileDir, headRelativeToModuleRoot).replace(/\\/g, '/');
|
|
125
|
+
|
|
126
|
+
header.relativePathToCurrentFile = relativePathToHeader;
|
|
127
|
+
}
|
|
104
128
|
|
|
105
129
|
// 如果是同一模块,使用相对路径;否则使用 <> 格式
|
|
106
130
|
handleModuleHeader(specFile, updateFile, header, importArray, !isSameModule);
|
|
@@ -125,57 +149,112 @@ function determineCurrentModuleName(filePath, packageInfo) {
|
|
|
125
149
|
}
|
|
126
150
|
|
|
127
151
|
/**
|
|
128
|
-
*
|
|
152
|
+
* 确定头文件信息(模块名、相对路径等)
|
|
129
153
|
* 从头文件路径查找 Package.swift,确定最近的 target 模块名
|
|
154
|
+
* 同时计算相对于当前文件的相对路径
|
|
155
|
+
* @param {string} specFile - 配置文件路径
|
|
156
|
+
* @param {object} header - 头文件信息对象
|
|
157
|
+
* @param {string} headRelativePath - 头文件相对路径(相对于模块根目录),可能来自标记或缓存
|
|
158
|
+
* @param {object} currentPackageInfo - 当前文件的 Package.swift 信息
|
|
159
|
+
* @param {string} currentModuleName - 当前模块名
|
|
130
160
|
*/
|
|
131
|
-
async function
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
}
|
|
161
|
+
async function determineHeaderInfo(specFile, header, headRelativePath, currentPackageInfo, currentModuleName) {
|
|
162
|
+
// 默认值
|
|
163
|
+
let moduleName = header.moduleName;
|
|
164
|
+
let relativePathToCurrentFile = null;
|
|
136
165
|
|
|
137
|
-
// 从头缓存中获取头文件的相对路径
|
|
138
|
-
const headRelativePath = headCache[header.headerName];
|
|
139
166
|
if (!headRelativePath) {
|
|
140
|
-
//
|
|
141
|
-
return
|
|
167
|
+
// 没有相对路径,使用从 headerLine 解析的模块名(可能不准确)
|
|
168
|
+
return {
|
|
169
|
+
moduleName: moduleName,
|
|
170
|
+
headRelativePath: null,
|
|
171
|
+
relativePathToCurrentFile: null
|
|
172
|
+
};
|
|
142
173
|
}
|
|
143
174
|
|
|
144
|
-
// ✅ specFile
|
|
145
|
-
|
|
146
|
-
const
|
|
175
|
+
// ✅ specFile 可能是根目录的 AutoSnippetRoot.boxspec.json,需要找到头文件所在的模块配置
|
|
176
|
+
// 尝试从根目录的配置文件查找头文件所在的模块
|
|
177
|
+
const rootSpecDir = path.dirname(specFile);
|
|
178
|
+
let headPath = null;
|
|
179
|
+
|
|
180
|
+
// 尝试多种可能的路径
|
|
181
|
+
// 1. 直接使用根目录的相对路径
|
|
182
|
+
headPath = path.join(rootSpecDir, headRelativePath);
|
|
183
|
+
|
|
184
|
+
// 2. 如果文件不存在,尝试从子模块中查找
|
|
185
|
+
if (!fs.existsSync(headPath)) {
|
|
186
|
+
// 从头文件路径向上查找 Package.swift
|
|
187
|
+
const headerPackagePath = await findPath.findPackageSwiftPath(headPath);
|
|
188
|
+
if (headerPackagePath) {
|
|
189
|
+
const headerPackageInfo = await findPath.parsePackageSwift(headerPackagePath);
|
|
190
|
+
if (headerPackageInfo) {
|
|
191
|
+
// 找到 Package.swift,使用它的路径作为模块根目录
|
|
192
|
+
const headerModuleRootDir = path.dirname(headerPackagePath);
|
|
193
|
+
headPath = path.join(headerModuleRootDir, headRelativePath);
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
}
|
|
147
197
|
|
|
148
198
|
// 从头文件路径向上查找 Package.swift
|
|
149
199
|
const headerPackagePath = await findPath.findPackageSwiftPath(headPath);
|
|
150
200
|
if (!headerPackagePath) {
|
|
151
201
|
// 找不到 Package.swift,使用从 headerLine 解析的模块名
|
|
152
|
-
return
|
|
202
|
+
return {
|
|
203
|
+
moduleName: moduleName,
|
|
204
|
+
headRelativePath: headRelativePath,
|
|
205
|
+
relativePathToCurrentFile: null
|
|
206
|
+
};
|
|
153
207
|
}
|
|
154
208
|
|
|
155
209
|
// 解析 Package.swift 获取模块信息
|
|
156
210
|
const headerPackageInfo = await findPath.parsePackageSwift(headerPackagePath);
|
|
157
211
|
if (!headerPackageInfo) {
|
|
158
|
-
return
|
|
212
|
+
return {
|
|
213
|
+
moduleName: moduleName,
|
|
214
|
+
headRelativePath: headRelativePath,
|
|
215
|
+
relativePathToCurrentFile: null
|
|
216
|
+
};
|
|
159
217
|
}
|
|
160
218
|
|
|
161
219
|
// ✅ 根据头文件路径确定最近的 target 模块名
|
|
162
|
-
|
|
220
|
+
moduleName = determineCurrentModuleName(headPath, headerPackageInfo);
|
|
221
|
+
|
|
222
|
+
// ✅ 如果是同一模块,计算相对于当前文件的相对路径
|
|
223
|
+
// 注意:这里需要知道当前文件的路径,所以需要从外部传入
|
|
224
|
+
// 但当前函数中还没有 updateFile,所以这部分逻辑在 handleHeaderLine 中完成
|
|
225
|
+
|
|
226
|
+
return {
|
|
227
|
+
moduleName: moduleName,
|
|
228
|
+
headRelativePath: headRelativePath,
|
|
229
|
+
relativePathToCurrentFile: null // 将在 handleHeaderLine 中计算
|
|
230
|
+
};
|
|
163
231
|
}
|
|
164
232
|
|
|
165
233
|
// isOuter区分模块内部引用""格式和模块外部引用<>格式
|
|
166
234
|
// ✅ SPM 模块:
|
|
167
|
-
// - isOuter = false:同一模块内部,使用相对路径 #import "Header.h"
|
|
235
|
+
// - isOuter = false:同一模块内部,使用相对路径 #import "relative/path/Header.h" 或 #import "Header.h"
|
|
168
236
|
// - isOuter = true:不同模块之间,使用 #import <ModuleName/Header.h>
|
|
169
237
|
function handleModuleHeader(specFile, updateFile, header, importArray, isOuter) {
|
|
170
|
-
// ✅ 根据 isOuter
|
|
171
|
-
|
|
238
|
+
// ✅ 根据 isOuter 选择不同的引入格式,用于检查是否已存在
|
|
239
|
+
let headNameToCheck;
|
|
240
|
+
if (isOuter) {
|
|
241
|
+
// 外部模块,使用 <> 格式
|
|
242
|
+
headNameToCheck = header.name; // <ModuleName/Header.h>
|
|
243
|
+
} else {
|
|
244
|
+
// 同一模块内部,使用相对路径
|
|
245
|
+
if (header.relativePathToCurrentFile) {
|
|
246
|
+
headNameToCheck = '"' + header.relativePathToCurrentFile + '"'; // "relative/path/Header.h"
|
|
247
|
+
} else {
|
|
248
|
+
headNameToCheck = header.headerStrName; // "Header.h"
|
|
249
|
+
}
|
|
250
|
+
}
|
|
172
251
|
const moduleName = isOuter ? header.specName : header.moduleStrName;
|
|
173
252
|
|
|
174
253
|
// 检查是否已经引入头文件
|
|
175
254
|
for (let i = 0; i < importArray.length; i++) {
|
|
176
255
|
const importHeader = importArray[i].split(importMark)[1].trim();
|
|
177
256
|
|
|
178
|
-
if (importHeader ===
|
|
257
|
+
if (importHeader === headNameToCheck) {
|
|
179
258
|
// 已经引入头文件
|
|
180
259
|
handelAddHeaderStatus(specFile, updateFile, header, true, false, isOuter);
|
|
181
260
|
return;
|
|
@@ -267,10 +346,21 @@ function removeMarkFromFile(updateFile, header, string) {
|
|
|
267
346
|
function addHeaderToFile(updateFile, header, isOuter) {
|
|
268
347
|
// ✅ 根据 isOuter 选择不同的引入格式
|
|
269
348
|
// isOuter = true: 使用 <> 格式(#import <ModuleName/Header.h>)
|
|
270
|
-
// isOuter = false: 使用相对路径(#import "Header.h")
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
349
|
+
// isOuter = false: 使用相对路径(#import "relative/path/Header.h" 或 #import "Header.h")
|
|
350
|
+
let importLine;
|
|
351
|
+
if (isOuter) {
|
|
352
|
+
// 外部模块,使用 <> 格式
|
|
353
|
+
importLine = importMark + ' ' + header.name; // <ModuleName/Header.h>
|
|
354
|
+
} else {
|
|
355
|
+
// 同一模块内部,使用相对路径
|
|
356
|
+
if (header.relativePathToCurrentFile) {
|
|
357
|
+
// 使用计算出的相对路径(如 "SubDir/Header.h" 或 "../Header.h")
|
|
358
|
+
importLine = importMark + ' "' + header.relativePathToCurrentFile + '"';
|
|
359
|
+
} else {
|
|
360
|
+
// 如果没有相对路径,使用文件名(如 "Header.h")
|
|
361
|
+
importLine = importMark + ' ' + header.headerStrName; // "Header.h"
|
|
362
|
+
}
|
|
363
|
+
}
|
|
274
364
|
|
|
275
365
|
readStream(updateFile, importLine, importMark);
|
|
276
366
|
checkDependency(updateFile, header.moduleName, '自动注入头文件完成。').catch(err => {
|
package/bin/install.js
CHANGED
|
@@ -30,6 +30,11 @@ function writeSingleSnippet(snippet, template) {
|
|
|
30
30
|
// 例如:<BDVideoPlayer/BDVideoCacheManager.h> 而不是 <BDVideoPlayer/UI/BDVideoPlayer/Code/BDVideoCacheManager.h>
|
|
31
31
|
const headerFileName = path.basename(extPlace['{headName}']);
|
|
32
32
|
let header = '<' + extPlace['{specName}'] + '/' + headerFileName + '>';
|
|
33
|
+
|
|
34
|
+
// ✅ 在标记中包含相对路径信息,格式:// ahead <Module/Header.h> relative/path/to/Header.h
|
|
35
|
+
// 这样即使没有缓存也能解析出完整信息
|
|
36
|
+
const headRelativePath = extPlace['{headName}']; // 相对于模块根目录的相对路径
|
|
37
|
+
header = header + ' ' + headRelativePath;
|
|
33
38
|
header = escapeString(header);
|
|
34
39
|
|
|
35
40
|
// swift只需要考虑工作空间是否引入
|
|
@@ -42,7 +47,7 @@ function writeSingleSnippet(snippet, template) {
|
|
|
42
47
|
extPlace['{completion}'] = extPlace['{completion}'] + 'Z';
|
|
43
48
|
extPlace['{summary}'] = extPlace['{summary}'] + header;
|
|
44
49
|
|
|
45
|
-
// 添加替换header
|
|
50
|
+
// 添加替换header标识位(格式:// ahead <Module/Header.h> relative/path/to/Header.h)
|
|
46
51
|
let array = ['// ahead ' + header];
|
|
47
52
|
extPlace['{content}'].forEach(element => {
|
|
48
53
|
array.push(element);
|
|
@@ -159,6 +164,11 @@ function addCodeSnippets(specFile, singleSnippet) {
|
|
|
159
164
|
// ✅ 从项目根目录找到模块,只使用模块名和头文件名
|
|
160
165
|
const headerFileName = path.basename(extPlace['{headName}']);
|
|
161
166
|
let header = '<' + extPlace['{specName}'] + '/' + headerFileName + '>';
|
|
167
|
+
|
|
168
|
+
// ✅ 在标记中包含相对路径信息,格式:// ahead <Module/Header.h> relative/path/to/Header.h
|
|
169
|
+
// 这样即使没有缓存也能解析出完整信息
|
|
170
|
+
const headRelativePath = extPlace['{headName}']; // 相对于模块根目录的相对路径
|
|
171
|
+
header = header + ' ' + headRelativePath;
|
|
162
172
|
header = escapeString(header);
|
|
163
173
|
|
|
164
174
|
// swift只需要考虑工作空间是否引入
|
package/bin/watch.js
CHANGED
|
@@ -14,7 +14,8 @@ const alinkMark = 'alink';
|
|
|
14
14
|
const wellMark = '#';
|
|
15
15
|
const atMark = '@';
|
|
16
16
|
|
|
17
|
-
|
|
17
|
+
// ✅ 更新正则以匹配包含相对路径的新格式:// ahead <Module/Header.h> relative/path/to/Header.h
|
|
18
|
+
const headerReg = /^\/\/ ahead <\w+\/\w+.h>(\s+.+)?$/;
|
|
18
19
|
const headerSwiftReg = /^\/\/ ahead \w+$/;
|
|
19
20
|
const importReg = /^\#import\s*<\w+\/\w+.h>$/;
|
|
20
21
|
const importSwiftReg = /^import\s*\w+$/;
|
|
@@ -22,10 +23,11 @@ const importSwiftReg = /^import\s*\w+$/;
|
|
|
22
23
|
let timeoutLink = null;
|
|
23
24
|
let timeoutHead = null;
|
|
24
25
|
|
|
25
|
-
function watchFileChange(specFile) {
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
26
|
+
function watchFileChange(specFile, watchRootPath) {
|
|
27
|
+
// ✅ 如果指定了监听根目录,使用它;否则使用当前工作目录
|
|
28
|
+
const filePath = watchRootPath || CMD_PATH;
|
|
29
|
+
console.log(`[watchFileChange] 监听目录: ${filePath}`);
|
|
30
|
+
console.log(`[watchFileChange] 配置文件: ${specFile}`);
|
|
29
31
|
let isReading = false;
|
|
30
32
|
|
|
31
33
|
fs.watch(filePath, {recursive: true}, (event, filename) => {
|