autosnippet 1.1.12 → 1.1.14
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/LICENSE +21 -0
- package/README.md +36 -36
- package/bin/asnip.js +67 -8
- package/bin/cache.js +7 -8
- package/bin/config.js +89 -0
- package/bin/create.js +151 -41
- package/bin/findPath.js +290 -128
- package/bin/injection.js +45 -76
- package/bin/install.js +131 -13
- package/package.json +8 -5
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2021 高雪峰
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,36 +1,36 @@
|
|
|
1
1
|
# AutoSnippet
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
一个 iOS 模块管理工具。使用命令行创建 Xcode 代码片段,生成 JSON 配置文件,并在代码仓库中与其他开发者共享。
|
|
4
4
|
|
|
5
|
-
##
|
|
5
|
+
## 安装
|
|
6
6
|
|
|
7
7
|
```bash
|
|
8
8
|
$ npm install -g autosnippet
|
|
9
9
|
```
|
|
10
10
|
|
|
11
|
-
##
|
|
11
|
+
## 命令选项
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
请在当前 Xcode 项目文件目录下使用以下所有命令。
|
|
14
14
|
|
|
15
15
|
### init
|
|
16
16
|
|
|
17
|
-
|
|
17
|
+
在 Xcode 项目的根目录执行此命令以创建工作空间:
|
|
18
18
|
|
|
19
19
|
```bash
|
|
20
20
|
$ asd init
|
|
21
21
|
```
|
|
22
22
|
|
|
23
|
-
|
|
23
|
+
创建工作空间时,会将子工作空间的 Snippet 配置信息收集到当前工作空间。
|
|
24
24
|
|
|
25
25
|
### create
|
|
26
26
|
|
|
27
|
-
|
|
27
|
+
创建 Xcode 代码片段的命令,在标记有 `// ACode` 代码的文件目录中:
|
|
28
28
|
|
|
29
29
|
```bash
|
|
30
30
|
$ asd c
|
|
31
31
|
```
|
|
32
32
|
|
|
33
|
-
|
|
33
|
+
代码示例:
|
|
34
34
|
|
|
35
35
|
```
|
|
36
36
|
// ACode
|
|
@@ -40,22 +40,22 @@ UIView *view = [[UIView alloc] init];
|
|
|
40
40
|
|
|
41
41
|
### install
|
|
42
42
|
|
|
43
|
-
|
|
43
|
+
将共享的代码片段添加到 Xcode 环境:
|
|
44
44
|
|
|
45
45
|
```bash
|
|
46
46
|
$ asd i
|
|
47
47
|
```
|
|
48
48
|
|
|
49
|
-
|
|
49
|
+
使用代码片段示例:
|
|
50
50
|
|
|
51
51
|
```
|
|
52
|
-
// view
|
|
52
|
+
// view 是创建时输入的代码键
|
|
53
53
|
@view
|
|
54
54
|
```
|
|
55
55
|
|
|
56
56
|
### share
|
|
57
57
|
|
|
58
|
-
|
|
58
|
+
共享本地代码片段:
|
|
59
59
|
|
|
60
60
|
```bash
|
|
61
61
|
$ asd s
|
|
@@ -63,60 +63,60 @@ $ asd s
|
|
|
63
63
|
|
|
64
64
|
### watch
|
|
65
65
|
|
|
66
|
-
|
|
66
|
+
在模块化项目中,识别代码片段并自动注入依赖头文件:
|
|
67
67
|
|
|
68
68
|
```bash
|
|
69
69
|
$ asd w
|
|
70
70
|
```
|
|
71
71
|
|
|
72
|
-
####
|
|
72
|
+
#### 追加头文件
|
|
73
73
|
|
|
74
|
-
|
|
74
|
+
开启监听后,如果想要追加头文件,请执行以下操作:
|
|
75
75
|
|
|
76
|
-
1.
|
|
77
|
-
2.
|
|
78
|
-
3. `Command + S`
|
|
76
|
+
1. 向下箭头选择代码片段的 headerVersion
|
|
77
|
+
2. 按 `Enter` 键
|
|
78
|
+
3. `Command + S` 保存文件
|
|
79
79
|
|
|
80
|
-
|
|
80
|
+
在 1 秒内,头文件会自动添加到文件头部。
|
|
81
81
|
|
|
82
|
-
####
|
|
82
|
+
#### 浏览器查看
|
|
83
83
|
|
|
84
|
-
|
|
84
|
+
开启监听后,如果想要在浏览器中查看模块的更多信息,请执行以下操作:
|
|
85
85
|
|
|
86
|
-
1.
|
|
87
|
-
2.
|
|
88
|
-
3. `Command + S`
|
|
86
|
+
1. 输入 `@` 和 `模块键`
|
|
87
|
+
2. 输入 `#` 和 `ALink`
|
|
88
|
+
3. `Command + S` 保存文件
|
|
89
89
|
|
|
90
|
-
|
|
90
|
+
会自动跳转到浏览器打开创建时配置的链接,如果没有链接则打开 README.md 文件。
|
|
91
91
|
|
|
92
|
-
|
|
92
|
+
使用 ALink 示例:
|
|
93
93
|
|
|
94
94
|
```
|
|
95
95
|
@view#ALink
|
|
96
96
|
```
|
|
97
97
|
|
|
98
|
-
##
|
|
98
|
+
## 其他
|
|
99
99
|
|
|
100
|
-
###
|
|
100
|
+
### 占位符快捷键
|
|
101
101
|
|
|
102
|
-
|
|
102
|
+
您也可以在代码片段中添加占位符,使用以下标签:
|
|
103
103
|
|
|
104
104
|
```
|
|
105
105
|
<#placeholder#>
|
|
106
106
|
```
|
|
107
107
|
|
|
108
|
-
|
|
108
|
+
例如:上面的占位符可以写成:
|
|
109
109
|
|
|
110
110
|
```
|
|
111
111
|
<#view: UIView#>
|
|
112
112
|
```
|
|
113
113
|
|
|
114
|
-
Xcode
|
|
114
|
+
Xcode 会检测 `<#` 和 `#>` 标记,并将它们之间的文本作为占位符。我们可以通过按 `Tab` 键在多个占位符之间切换。
|
|
115
115
|
|
|
116
|
-
|
|
116
|
+
当有多个相同的占位符时,使用 `⌥⌘E` 连续选择多个占位符:
|
|
117
117
|
|
|
118
|
-
1.
|
|
119
|
-
2. `⌥⌘E`
|
|
120
|
-
3.
|
|
118
|
+
1. 选择一个占位符
|
|
119
|
+
2. `⌥⌘E` 选择下一个占位符,`⌥⇧⌘E` 选择上一个占位符
|
|
120
|
+
3. 输入修改的内容,所有选中的占位符都会被修改
|
|
121
121
|
|
|
122
|
-
|
|
122
|
+
感谢使用。
|
package/bin/asnip.js
CHANGED
|
@@ -15,8 +15,66 @@ const watch = require('./watch.js');
|
|
|
15
15
|
const cache = require('./cache.js');
|
|
16
16
|
const share = require('./share.js');
|
|
17
17
|
const init = require('./init.js');
|
|
18
|
+
const config = require('./config.js');
|
|
18
19
|
|
|
19
|
-
|
|
20
|
+
// 获取配置文件路径(测试模式优先使用 BiliDemo 配置)
|
|
21
|
+
function getSpecFile(callback) {
|
|
22
|
+
// 测试模式下,优先使用 BiliDemo 的配置文件
|
|
23
|
+
if (config.isTestMode()) {
|
|
24
|
+
const configPath = config.getConfigPath();
|
|
25
|
+
if (configPath && fs.existsSync(configPath)) {
|
|
26
|
+
callback(configPath);
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// 否则使用原有的查找逻辑
|
|
32
|
+
findPath.findASSpecPath(CMD_PATH, callback);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* 先查找包含 // ACode 标记的文件,找到后再询问
|
|
37
|
+
*/
|
|
38
|
+
async function findAndAsk(specFile) {
|
|
39
|
+
console.log('正在查找包含 // ACode 标记的文件...\n');
|
|
40
|
+
|
|
41
|
+
const filesWithACode = await create.findFilesWithACode(CMD_PATH);
|
|
42
|
+
|
|
43
|
+
if (filesWithACode.length === 0) {
|
|
44
|
+
console.log('未找到包含 // ACode 标记的文件。');
|
|
45
|
+
console.log('请在代码中添加 // ACode 标记,例如:');
|
|
46
|
+
console.log('');
|
|
47
|
+
console.log('// ACode');
|
|
48
|
+
console.log('UIView *view = [[UIView alloc] init];');
|
|
49
|
+
console.log('// ACode');
|
|
50
|
+
console.log('');
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// 显示找到的文件
|
|
55
|
+
console.log(`找到 ${filesWithACode.length} 个包含 // ACode 标记的文件:\n`);
|
|
56
|
+
filesWithACode.forEach((file, index) => {
|
|
57
|
+
console.log(` ${index + 1}. ${file.name} (第 ${file.line} 行)`);
|
|
58
|
+
});
|
|
59
|
+
console.log('');
|
|
60
|
+
|
|
61
|
+
// 如果只有一个文件,直接使用;如果有多个,让用户选择
|
|
62
|
+
let selectedFile = null;
|
|
63
|
+
if (filesWithACode.length === 1) {
|
|
64
|
+
selectedFile = filesWithACode[0].path;
|
|
65
|
+
console.log(`将使用文件: ${filesWithACode[0].name}\n`);
|
|
66
|
+
} else {
|
|
67
|
+
// 多个文件时,让用户选择(这里简化处理,使用第一个)
|
|
68
|
+
// 可以后续扩展为让用户选择
|
|
69
|
+
selectedFile = filesWithACode[0].path;
|
|
70
|
+
console.log(`将使用第一个文件: ${filesWithACode[0].name}\n`);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// 找到标记后,开始询问
|
|
74
|
+
askQuestions(specFile, selectedFile);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function askQuestions(specFile, selectedFilePath) {
|
|
20
78
|
// 开始问问题
|
|
21
79
|
const questions = [{
|
|
22
80
|
type: 'input',
|
|
@@ -106,7 +164,8 @@ function askQuestions(specFile) {
|
|
|
106
164
|
];
|
|
107
165
|
|
|
108
166
|
inquirer.prompt(questions).then((answers) => {
|
|
109
|
-
|
|
167
|
+
// 将选中的文件路径传递给 createCodeSnippets
|
|
168
|
+
create.createCodeSnippets(specFile, answers, null, selectedFilePath);
|
|
110
169
|
});
|
|
111
170
|
}
|
|
112
171
|
|
|
@@ -127,7 +186,7 @@ commander
|
|
|
127
186
|
.command('i')
|
|
128
187
|
.description('add the shared Snippet to the Xcode environment')
|
|
129
188
|
.action(() => {
|
|
130
|
-
|
|
189
|
+
getSpecFile(function (specFile) {
|
|
131
190
|
install.addCodeSnippets(specFile);
|
|
132
191
|
});
|
|
133
192
|
});
|
|
@@ -136,7 +195,7 @@ commander
|
|
|
136
195
|
.command('s')
|
|
137
196
|
.description('share local Xcode Snippet')
|
|
138
197
|
.action(() => {
|
|
139
|
-
|
|
198
|
+
getSpecFile(function (specFile) {
|
|
140
199
|
share.shareCodeSnippets(specFile);
|
|
141
200
|
});
|
|
142
201
|
});
|
|
@@ -145,8 +204,8 @@ commander
|
|
|
145
204
|
.command('c')
|
|
146
205
|
.description('create an Xcode Snippet, in the file directory marked with `// ACode` code')
|
|
147
206
|
.action(() => {
|
|
148
|
-
|
|
149
|
-
|
|
207
|
+
getSpecFile(function (specFile) {
|
|
208
|
+
findAndAsk(specFile);
|
|
150
209
|
});
|
|
151
210
|
});
|
|
152
211
|
|
|
@@ -154,7 +213,7 @@ commander
|
|
|
154
213
|
.command('u <word> [key] [value]')
|
|
155
214
|
.description('modify the `// ACode` code corresponding to `word`')
|
|
156
215
|
.action((word, key, value) => {
|
|
157
|
-
|
|
216
|
+
getSpecFile(function (specFile) {
|
|
158
217
|
create.updateCodeSnippets(specFile, word, key, value);
|
|
159
218
|
});
|
|
160
219
|
});
|
|
@@ -163,7 +222,7 @@ commander
|
|
|
163
222
|
.command('w')
|
|
164
223
|
.description('recognize that Snippet automatically injects dependency header files')
|
|
165
224
|
.action(() => {
|
|
166
|
-
|
|
225
|
+
getSpecFile(function (specFile) {
|
|
167
226
|
install.addCodeSnippets(specFile);
|
|
168
227
|
watch.watchFileChange(specFile);
|
|
169
228
|
});
|
package/bin/cache.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
const fs = require('fs');
|
|
4
|
+
const path = require('path');
|
|
5
|
+
const config = require('./config.js');
|
|
4
6
|
|
|
5
7
|
const SpecCache = 'SpecCache_';
|
|
6
8
|
const KeysCache = 'KeysCache_';
|
|
@@ -114,15 +116,12 @@ async function getHeadCache(specFile) {
|
|
|
114
116
|
function getFilePathFromHolderPath(key, specFile) {
|
|
115
117
|
const pathBuff = Buffer.from(specFile, 'utf-8');
|
|
116
118
|
const fileName = key + pathBuff.toString('base64') + '.json';
|
|
117
|
-
|
|
119
|
+
|
|
120
|
+
// ✅ 使用配置模块获取缓存路径
|
|
121
|
+
const cachePath = config.getCachePath();
|
|
122
|
+
const filePath = path.join(cachePath, fileName);
|
|
118
123
|
|
|
119
|
-
|
|
120
|
-
fs.accessSync(filePath, fs.F_OK);
|
|
121
|
-
} catch (err) {
|
|
122
|
-
fs.mkdirSync(filePath);
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
return filePath + fileName;
|
|
124
|
+
return filePath;
|
|
126
125
|
}
|
|
127
126
|
|
|
128
127
|
exports.updateCache = updateCache;
|
package/bin/config.js
ADDED
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
const path = require('path');
|
|
4
|
+
const fs = require('fs');
|
|
5
|
+
|
|
6
|
+
// 检测是否为测试模式
|
|
7
|
+
// 通过环境变量 AUTOSNIPPET_TEST_MODE 或检测 BiliDemo 目录
|
|
8
|
+
function isTestMode() {
|
|
9
|
+
// 优先检查环境变量
|
|
10
|
+
if (process.env.AUTOSNIPPET_TEST_MODE === 'true') {
|
|
11
|
+
return true;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
// 检查当前工作目录是否在 BiliDemo 项目内
|
|
15
|
+
const cwd = process.cwd();
|
|
16
|
+
if (cwd.includes('BiliDemo')) {
|
|
17
|
+
return true;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
return false;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// 获取 BiliDemo 项目根目录
|
|
24
|
+
function getBiliDemoRoot() {
|
|
25
|
+
const cwd = process.cwd();
|
|
26
|
+
const parts = cwd.split(path.sep);
|
|
27
|
+
|
|
28
|
+
for (let i = parts.length; i > 0; i--) {
|
|
29
|
+
const testPath = parts.slice(0, i).join(path.sep);
|
|
30
|
+
if (fs.existsSync(path.join(testPath, 'BiliDili', 'AutoSnippet.boxspec.json'))) {
|
|
31
|
+
return testPath;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
return null;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// 获取配置文件路径
|
|
39
|
+
function getConfigPath() {
|
|
40
|
+
if (isTestMode()) {
|
|
41
|
+
const biliDemoRoot = getBiliDemoRoot();
|
|
42
|
+
if (biliDemoRoot) {
|
|
43
|
+
return path.join(biliDemoRoot, 'BiliDili', 'AutoSnippet.boxspec.json');
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
return null;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// 获取代码片段输出路径(测试模式写入 AutoSnippetCache,生产模式写入 Xcode)
|
|
50
|
+
function getSnippetsPath() {
|
|
51
|
+
if (isTestMode()) {
|
|
52
|
+
// ✅ 测试模式:写入 AutoSnippetCache/Snippets/
|
|
53
|
+
// __dirname: AutoSnippet/bin
|
|
54
|
+
// 目标路径: AutoSnippet/AutoSnippetCache/Snippets
|
|
55
|
+
const cachePath = path.join(__dirname, '../../AutoSnippetCache/Snippets');
|
|
56
|
+
try {
|
|
57
|
+
fs.accessSync(cachePath, fs.constants.F_OK);
|
|
58
|
+
} catch (err) {
|
|
59
|
+
fs.mkdirSync(cachePath, { recursive: true });
|
|
60
|
+
}
|
|
61
|
+
return cachePath;
|
|
62
|
+
} else {
|
|
63
|
+
// 生产模式:写入 Xcode CodeSnippets 目录
|
|
64
|
+
const USER_HOME = process.env.HOME || process.env.USERPROFILE;
|
|
65
|
+
return path.join(USER_HOME, 'Library/Developer/Xcode/UserData/CodeSnippets');
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// 获取缓存目录路径
|
|
70
|
+
function getCachePath() {
|
|
71
|
+
// ✅ 缓存始终在 AutoSnippetCache 目录
|
|
72
|
+
// __dirname: AutoSnippet/bin
|
|
73
|
+
// 目标路径: AutoSnippet/AutoSnippetCache
|
|
74
|
+
const cachePath = path.join(__dirname, '../../AutoSnippetCache');
|
|
75
|
+
try {
|
|
76
|
+
fs.accessSync(cachePath, fs.constants.F_OK);
|
|
77
|
+
} catch (err) {
|
|
78
|
+
fs.mkdirSync(cachePath, { recursive: true });
|
|
79
|
+
}
|
|
80
|
+
return cachePath;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
module.exports = {
|
|
84
|
+
isTestMode,
|
|
85
|
+
getBiliDemoRoot,
|
|
86
|
+
getConfigPath,
|
|
87
|
+
getSnippetsPath,
|
|
88
|
+
getCachePath
|
|
89
|
+
};
|
package/bin/create.js
CHANGED
|
@@ -6,9 +6,32 @@ const readline = require('readline');
|
|
|
6
6
|
const cache = require('./cache.js');
|
|
7
7
|
const findPath = require('./findPath.js');
|
|
8
8
|
const install = require('./install.js');
|
|
9
|
+
// 全局常量
|
|
10
|
+
const README_NAME = 'readme.md';
|
|
9
11
|
// 全局路径
|
|
10
12
|
const CMD_PATH = process.cwd();
|
|
11
13
|
|
|
14
|
+
/**
|
|
15
|
+
* 根据文件路径确定模块名(SPM)
|
|
16
|
+
*/
|
|
17
|
+
function determineModuleName(filePath, packageInfo) {
|
|
18
|
+
// 从路径中提取模块名
|
|
19
|
+
// 例如:Services/Services/BDNetworkControl/Code/... -> BDNetworkControl
|
|
20
|
+
const packagePath = packageInfo.path;
|
|
21
|
+
const relativePath = path.relative(packagePath, filePath);
|
|
22
|
+
const segments = relativePath.split(path.sep);
|
|
23
|
+
|
|
24
|
+
// 查找匹配的 target
|
|
25
|
+
for (const segment of segments) {
|
|
26
|
+
if (packageInfo.targets.includes(segment)) {
|
|
27
|
+
return segment;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// 如果找不到,使用第一个 target
|
|
32
|
+
return packageInfo.targets[0] || packageInfo.name;
|
|
33
|
+
}
|
|
34
|
+
|
|
12
35
|
function updateCodeSnippets(specFile, word, key, value) {
|
|
13
36
|
if (key && key !== 'title' && key !== 'link' && key !== 'summary') {
|
|
14
37
|
console.log('此项属性不存在或不可修改。');
|
|
@@ -54,12 +77,61 @@ function updateCodeSnippets(specFile, word, key, value) {
|
|
|
54
77
|
}
|
|
55
78
|
}
|
|
56
79
|
|
|
57
|
-
|
|
80
|
+
/**
|
|
81
|
+
* 查找包含 // ACode 标记的文件
|
|
82
|
+
*/
|
|
83
|
+
async function findFilesWithACode(filePath) {
|
|
84
|
+
const filesWithACode = [];
|
|
85
|
+
|
|
86
|
+
try {
|
|
87
|
+
const files = await fs.promises.readdir(filePath);
|
|
88
|
+
|
|
89
|
+
for (const filename of files) {
|
|
90
|
+
const filedir = path.join(filePath, filename);
|
|
91
|
+
try {
|
|
92
|
+
const stats = await fs.promises.lstat(filedir);
|
|
93
|
+
if (stats.isFile()) {
|
|
94
|
+
// 只检查源代码文件
|
|
95
|
+
if (filename.endsWith('.m') || filename.endsWith('.h') || filename.endsWith('.swift')) {
|
|
96
|
+
const content = await fs.promises.readFile(filedir, 'utf8');
|
|
97
|
+
const lines = content.split('\n');
|
|
98
|
+
|
|
99
|
+
// 检查是否包含 // ACode 标记
|
|
100
|
+
for (let i = 0; i < lines.length; i++) {
|
|
101
|
+
if (lines[i].trim().toLowerCase() === '// acode') {
|
|
102
|
+
filesWithACode.push({
|
|
103
|
+
path: filedir,
|
|
104
|
+
name: filename,
|
|
105
|
+
line: i + 1
|
|
106
|
+
});
|
|
107
|
+
break; // 找到标记后跳出,每个文件只记录一次
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
} catch (err) {
|
|
113
|
+
// 忽略无法读取的文件
|
|
114
|
+
continue;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
} catch (err) {
|
|
118
|
+
console.error('Error reading directory:', err);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
return filesWithACode;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function createCodeSnippets(specFile, answers, updateSnippet, selectedFilePath) {
|
|
58
125
|
let snippet = updateSnippet;
|
|
59
126
|
let isHaveHeader = snippet === null ? false : (snippet['{headName}'] !== undefined);
|
|
60
127
|
|
|
61
128
|
if (snippet === null) {
|
|
62
|
-
|
|
129
|
+
// ✅ 处理 completion_more(可能是数组或字符串)
|
|
130
|
+
const completionMoreStr = Array.isArray(answers.completion_more)
|
|
131
|
+
? answers.completion_more.join('')
|
|
132
|
+
: answers.completion_more;
|
|
133
|
+
|
|
134
|
+
const answersKeys = answers.completion_first + completionMoreStr + answers.title;
|
|
63
135
|
const answersIdBuff = Buffer.from(answersKeys, 'utf-8');
|
|
64
136
|
const identifier = 'AutoSnip_' + answersIdBuff.toString('base64').replace(/\//g, '');
|
|
65
137
|
|
|
@@ -67,7 +139,7 @@ function createCodeSnippets(specFile, answers, updateSnippet) {
|
|
|
67
139
|
'{identifier}': identifier,
|
|
68
140
|
'{title}': answers.title,
|
|
69
141
|
'{completionKey}': answers.completion_first,
|
|
70
|
-
'{completion}': '@' + answers.completion_first +
|
|
142
|
+
'{completion}': '@' + answers.completion_first + completionMoreStr + '@Moudle',
|
|
71
143
|
'{summary}': answers.summary,
|
|
72
144
|
'{language}': 'Xcode.SourceCodeLanguage.Objective-C',
|
|
73
145
|
};
|
|
@@ -78,31 +150,39 @@ function createCodeSnippets(specFile, answers, updateSnippet) {
|
|
|
78
150
|
isHaveHeader = answers.header;
|
|
79
151
|
}
|
|
80
152
|
|
|
81
|
-
|
|
153
|
+
// ✅ 如果指定了文件路径,直接使用;否则查找当前目录下的所有文件
|
|
82
154
|
let filePathArr = [];
|
|
155
|
+
|
|
156
|
+
if (selectedFilePath) {
|
|
157
|
+
// 使用指定的文件
|
|
158
|
+
filePathArr = [selectedFilePath];
|
|
159
|
+
readStream(specFile, filePathArr, snippet, isHaveHeader);
|
|
160
|
+
} else {
|
|
161
|
+
// 原有逻辑:查找当前目录下的所有文件
|
|
162
|
+
const filePath = CMD_PATH;
|
|
163
|
+
fs.readdir(filePath, function (err, files) {
|
|
164
|
+
if (err) {
|
|
165
|
+
console.log(err);
|
|
166
|
+
return;
|
|
167
|
+
}
|
|
83
168
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
const isFile = stats.isFile();
|
|
96
|
-
if (isFile) {
|
|
97
|
-
filePathArr.push(filedir);
|
|
169
|
+
files.forEach(function (filename) {
|
|
170
|
+
const filedir = path.join(filePath, filename);
|
|
171
|
+
try {
|
|
172
|
+
// 读取路径是否为文件
|
|
173
|
+
const stats = fs.lstatSync(filedir);
|
|
174
|
+
const isFile = stats.isFile();
|
|
175
|
+
if (isFile) {
|
|
176
|
+
filePathArr.push(filedir);
|
|
177
|
+
}
|
|
178
|
+
} catch (err) {
|
|
179
|
+
console.error(err);
|
|
98
180
|
}
|
|
99
|
-
}
|
|
100
|
-
console.error(err);
|
|
101
|
-
}
|
|
102
|
-
});
|
|
181
|
+
});
|
|
103
182
|
|
|
104
|
-
|
|
105
|
-
|
|
183
|
+
readStream(specFile, filePathArr, snippet, isHaveHeader);
|
|
184
|
+
});
|
|
185
|
+
}
|
|
106
186
|
}
|
|
107
187
|
|
|
108
188
|
function readStream(specFile, filePathArr, snippet, isHaveHeader) {
|
|
@@ -152,24 +232,52 @@ function readStream(specFile, filePathArr, snippet, isHaveHeader) {
|
|
|
152
232
|
const specSlashIndex = specFile.lastIndexOf('/');
|
|
153
233
|
const specFilePath = specFile.substring(0, specSlashIndex + 1);
|
|
154
234
|
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
235
|
+
// ✅ 使用 SPM 的 Package.swift 查找(替代 .boxspec)
|
|
236
|
+
findPath.findPackageSwiftPath(thePath).then(async function (packagePath) {
|
|
237
|
+
if (!packagePath) {
|
|
238
|
+
console.log('未找到 Package.swift 文件,请检查路径。');
|
|
159
239
|
snippet['{content}'] = codeList;
|
|
160
|
-
snippet
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
if (headerPath) {
|
|
164
|
-
snippet['{specHeadPath}'] = encodeURI(headerPath.replace(specFilePath, ''));
|
|
165
|
-
}
|
|
240
|
+
saveFromFile(specFile, snippet);
|
|
241
|
+
return;
|
|
242
|
+
}
|
|
166
243
|
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
}
|
|
244
|
+
// 解析 Package.swift 获取模块信息
|
|
245
|
+
const packageInfo = await findPath.parsePackageSwift(packagePath);
|
|
246
|
+
if (!packageInfo) {
|
|
247
|
+
snippet['{content}'] = codeList;
|
|
171
248
|
saveFromFile(specFile, snippet);
|
|
172
|
-
|
|
249
|
+
return;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
// 根据当前文件路径确定模块名
|
|
253
|
+
const moduleName = determineModuleName(filePath, packageInfo);
|
|
254
|
+
const headerName = fileName.substring(0, fileName.length - 2); // 移除 .h
|
|
255
|
+
|
|
256
|
+
// ✅ 查找头文件(适配 SPM 的 include/ModuleName/ 结构)
|
|
257
|
+
const headerPath = await findPath.findSubHeaderPath(packageInfo.path, headerName, moduleName);
|
|
258
|
+
|
|
259
|
+
snippet['{content}'] = codeList;
|
|
260
|
+
snippet['{specName}'] = moduleName;
|
|
261
|
+
snippet['{headName}'] = fileName;
|
|
262
|
+
|
|
263
|
+
if (headerPath) {
|
|
264
|
+
snippet['{specHeadPath}'] = encodeURI(headerPath.replace(specFilePath, ''));
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
// 查找 README.md
|
|
268
|
+
try {
|
|
269
|
+
const readmePath = path.join(packageInfo.path, README_NAME);
|
|
270
|
+
await fs.promises.access(readmePath);
|
|
271
|
+
snippet['{readme}'] = encodeURI(readmePath.replace(specFilePath, ''));
|
|
272
|
+
} catch {
|
|
273
|
+
// README.md 不存在,跳过
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
saveFromFile(specFile, snippet);
|
|
277
|
+
}).catch(function (err) {
|
|
278
|
+
console.error('Error finding Package.swift:', err);
|
|
279
|
+
snippet['{content}'] = codeList;
|
|
280
|
+
saveFromFile(specFile, snippet);
|
|
173
281
|
});
|
|
174
282
|
} else {
|
|
175
283
|
snippet['{content}'] = codeList;
|
|
@@ -222,7 +330,8 @@ function saveFromFile(specFile, snippet) {
|
|
|
222
330
|
console.log(err);
|
|
223
331
|
}
|
|
224
332
|
cache.updateCache(specFile, content);
|
|
225
|
-
|
|
333
|
+
// ✅ 只写入刚创建的代码片段,而不是所有代码片段
|
|
334
|
+
install.addCodeSnippets(specFile, snippet);
|
|
226
335
|
}
|
|
227
336
|
}
|
|
228
337
|
}
|
|
@@ -258,4 +367,5 @@ function escapeString(string) {
|
|
|
258
367
|
|
|
259
368
|
exports.createCodeSnippets = createCodeSnippets;
|
|
260
369
|
exports.updateCodeSnippets = updateCodeSnippets;
|
|
261
|
-
exports.saveFromFile = saveFromFile;
|
|
370
|
+
exports.saveFromFile = saveFromFile;
|
|
371
|
+
exports.findFilesWithACode = findFilesWithACode;
|