autosnippet 1.1.13 → 1.1.15

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 CHANGED
@@ -1,36 +1,36 @@
1
1
  # AutoSnippet
2
2
 
3
- A iOS module management tool. Use the command line to create an Xcode Snippet, generate a JSON file, and share it with other developers in the code repository.
3
+ 一个 iOS 模块管理工具。使用命令行创建 Xcode 代码片段,生成 JSON 配置文件,并在代码仓库中与其他开发者共享。
4
4
 
5
- ## Installation
5
+ ## 安装
6
6
 
7
7
  ```bash
8
8
  $ npm install -g autosnippet
9
9
  ```
10
10
 
11
- ## Options
11
+ ## 命令选项
12
12
 
13
- Please use all the following commands in the current Xcode project file directory.
13
+ 请在当前 Xcode 项目文件目录下使用以下所有命令。
14
14
 
15
15
  ### init
16
16
 
17
- Execute this command in the root directory of the Xcode project to create a workspace:
17
+ Xcode 项目的根目录执行此命令以创建工作空间:
18
18
 
19
19
  ```bash
20
20
  $ asd init
21
21
  ```
22
22
 
23
- When creating a workspace, the Snippet configuration information of the sub-workspace will be collected to the current workspace.
23
+ 创建工作空间时,会将子工作空间的 Snippet 配置信息收集到当前工作空间。
24
24
 
25
25
  ### create
26
26
 
27
- Command to create an Xcode Snippet, in the file directory marked with `// ACode` code:
27
+ 创建 Xcode 代码片段的命令,在标记有 `// ACode` 代码的文件目录中:
28
28
 
29
29
  ```bash
30
30
  $ asd c
31
31
  ```
32
32
 
33
- Code like this:
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
- Add the shared Snippet to the Xcode environment:
43
+ 将共享的代码片段添加到 Xcode 环境:
44
44
 
45
45
  ```bash
46
46
  $ asd i
47
47
  ```
48
48
 
49
- Use code Snippet like this:
49
+ 使用代码片段示例:
50
50
 
51
51
  ```
52
- // view is code key entered when creating
52
+ // view 是创建时输入的代码键
53
53
  @view
54
54
  ```
55
55
 
56
56
  ### share
57
57
 
58
- Share local Snippet:
58
+ 共享本地代码片段:
59
59
 
60
60
  ```bash
61
61
  $ asd s
@@ -63,60 +63,60 @@ $ asd s
63
63
 
64
64
  ### watch
65
65
 
66
- In modular projects, recognize that Snippet automatically injects dependency header files:
66
+ 在模块化项目中,识别代码片段并自动注入依赖头文件:
67
67
 
68
68
  ```bash
69
69
  $ asd w
70
70
  ```
71
71
 
72
- #### Append header file
72
+ #### 追加头文件
73
73
 
74
- After the watch is turned on, if you want to append the header file, perform the following operations:
74
+ 开启监听后,如果想要追加头文件,请执行以下操作:
75
75
 
76
- 1. Down arrow to select the headerVersion of Snippet
77
- 2. Press the `Enter`
78
- 3. `Command + S` save file
76
+ 1. 向下箭头选择代码片段的 headerVersion
77
+ 2. `Enter`
78
+ 3. `Command + S` 保存文件
79
79
 
80
- Within 1 second, the header file is automatically added to the file header.
80
+ 1 秒内,头文件会自动添加到文件头部。
81
81
 
82
- #### Browser view
82
+ #### 浏览器查看
83
83
 
84
- After the watch is turned on, if you want to view more information about the module in the browser, perform the following operations:
84
+ 开启监听后,如果想要在浏览器中查看模块的更多信息,请执行以下操作:
85
85
 
86
- 1. Enter `@` and `module key`
87
- 2. Enter `#` and `ALink`
88
- 3. `Command + S` save file
86
+ 1. 输入 `@` `模块键`
87
+ 2. 输入 `#` `ALink`
88
+ 3. `Command + S` 保存文件
89
89
 
90
- Automatically jump to the browser to open the link configured during creation, and open the README.md file without a link.
90
+ 会自动跳转到浏览器打开创建时配置的链接,如果没有链接则打开 README.md 文件。
91
91
 
92
- Use ALink like this:
92
+ 使用 ALink 示例:
93
93
 
94
94
  ```
95
95
  @view#ALink
96
96
  ```
97
97
 
98
- ## Other
98
+ ## 其他
99
99
 
100
- ### Shortcuts for placeholders
100
+ ### 占位符快捷键
101
101
 
102
- You can add a placeholder in your snippets too using following tag:
102
+ 您也可以在代码片段中添加占位符,使用以下标签:
103
103
 
104
104
  ```
105
105
  <#placeholder#>
106
106
  ```
107
107
 
108
- E.g: the above placeholder can be written as:
108
+ 例如:上面的占位符可以写成:
109
109
 
110
110
  ```
111
111
  <#view: UIView#>
112
112
  ```
113
113
 
114
- Xcode detects <# and #> tokens and will make the text between them a placeholder. We can switch between multiple placeholders by pressing `Tab` key.
114
+ Xcode 会检测 `<#` `#>` 标记,并将它们之间的文本作为占位符。我们可以通过按 `Tab` 键在多个占位符之间切换。
115
115
 
116
- When there are multiple same placeholders, use `⌥⌘E` to select multiple placeholders continuously:
116
+ 当有多个相同的占位符时,使用 `⌥⌘E` 连续选择多个占位符:
117
117
 
118
- 1. Select a placeholder
119
- 2. `⌥⌘E` selects the next placeholder, `⌥⇧⌘E` selects the previous placeholder
120
- 3. Enter the modified content, all selected placeholders will be modified
118
+ 1. 选择一个占位符
119
+ 2. `⌥⌘E` 选择下一个占位符,`⌥⇧⌘E` 选择上一个占位符
120
+ 3. 输入修改的内容,所有选中的占位符都会被修改
121
121
 
122
- Thanks.
122
+ 感谢使用。
package/bin/asnip.js CHANGED
@@ -15,8 +15,57 @@ 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
- function askQuestions(specFile) {
20
+ // 获取配置文件路径
21
+ function getSpecFile(callback) {
22
+ // 向上查找 AutoSnippet.boxspec.json 配置文件
23
+ findPath.findASSpecPath(CMD_PATH, callback);
24
+ }
25
+
26
+ /**
27
+ * 先查找包含 // ACode 标记的文件,找到后再询问
28
+ */
29
+ async function findAndAsk(specFile) {
30
+ console.log('正在查找包含 // ACode 标记的文件...\n');
31
+
32
+ const filesWithACode = await create.findFilesWithACode(CMD_PATH);
33
+
34
+ if (filesWithACode.length === 0) {
35
+ console.log('未找到包含 // ACode 标记的文件。');
36
+ console.log('请在代码中添加 // ACode 标记,例如:');
37
+ console.log('');
38
+ console.log('// ACode');
39
+ console.log('UIView *view = [[UIView alloc] init];');
40
+ console.log('// ACode');
41
+ console.log('');
42
+ return;
43
+ }
44
+
45
+ // 显示找到的文件
46
+ console.log(`找到 ${filesWithACode.length} 个包含 // ACode 标记的文件:\n`);
47
+ filesWithACode.forEach((file, index) => {
48
+ console.log(` ${index + 1}. ${file.name} (第 ${file.line} 行)`);
49
+ });
50
+ console.log('');
51
+
52
+ // 如果只有一个文件,直接使用;如果有多个,让用户选择
53
+ let selectedFile = null;
54
+ if (filesWithACode.length === 1) {
55
+ selectedFile = filesWithACode[0].path;
56
+ console.log(`将使用文件: ${filesWithACode[0].name}\n`);
57
+ } else {
58
+ // 多个文件时,让用户选择(这里简化处理,使用第一个)
59
+ // 可以后续扩展为让用户选择
60
+ selectedFile = filesWithACode[0].path;
61
+ console.log(`将使用第一个文件: ${filesWithACode[0].name}\n`);
62
+ }
63
+
64
+ // 找到标记后,开始询问
65
+ askQuestions(specFile, selectedFile);
66
+ }
67
+
68
+ function askQuestions(specFile, selectedFilePath) {
20
69
  // 开始问问题
21
70
  const questions = [{
22
71
  type: 'input',
@@ -106,7 +155,8 @@ function askQuestions(specFile) {
106
155
  ];
107
156
 
108
157
  inquirer.prompt(questions).then((answers) => {
109
- create.createCodeSnippets(specFile, answers, null);
158
+ // 将选中的文件路径传递给 createCodeSnippets
159
+ create.createCodeSnippets(specFile, answers, null, selectedFilePath);
110
160
  });
111
161
  }
112
162
 
@@ -127,7 +177,7 @@ commander
127
177
  .command('i')
128
178
  .description('add the shared Snippet to the Xcode environment')
129
179
  .action(() => {
130
- findPath.findASSpecPath(CMD_PATH, function (specFile) {
180
+ getSpecFile(function (specFile) {
131
181
  install.addCodeSnippets(specFile);
132
182
  });
133
183
  });
@@ -136,7 +186,7 @@ commander
136
186
  .command('s')
137
187
  .description('share local Xcode Snippet')
138
188
  .action(() => {
139
- findPath.findASSpecPath(CMD_PATH, function (specFile) {
189
+ getSpecFile(function (specFile) {
140
190
  share.shareCodeSnippets(specFile);
141
191
  });
142
192
  });
@@ -145,8 +195,8 @@ commander
145
195
  .command('c')
146
196
  .description('create an Xcode Snippet, in the file directory marked with `// ACode` code')
147
197
  .action(() => {
148
- findPath.findASSpecPath(CMD_PATH, function (specFile) {
149
- askQuestions(specFile);
198
+ getSpecFile(function (specFile) {
199
+ findAndAsk(specFile);
150
200
  });
151
201
  });
152
202
 
@@ -154,7 +204,7 @@ commander
154
204
  .command('u <word> [key] [value]')
155
205
  .description('modify the `// ACode` code corresponding to `word`')
156
206
  .action((word, key, value) => {
157
- findPath.findASSpecPath(CMD_PATH, function (specFile) {
207
+ getSpecFile(function (specFile) {
158
208
  create.updateCodeSnippets(specFile, word, key, value);
159
209
  });
160
210
  });
@@ -163,7 +213,7 @@ commander
163
213
  .command('w')
164
214
  .description('recognize that Snippet automatically injects dependency header files')
165
215
  .action(() => {
166
- findPath.findASSpecPath(CMD_PATH, function (specFile) {
216
+ getSpecFile(function (specFile) {
167
217
  install.addCodeSnippets(specFile);
168
218
  watch.watchFileChange(specFile);
169
219
  });
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
- const filePath = __dirname + '/../../AutoSnippetCache/';
119
+
120
+ // ✅ 使用配置模块获取缓存路径
121
+ const cachePath = config.getCachePath();
122
+ const filePath = path.join(cachePath, fileName);
118
123
 
119
- try {
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,39 @@
1
+ #!/usr/bin/env node
2
+
3
+ const path = require('path');
4
+ const fs = require('fs');
5
+
6
+ // 获取代码片段输出路径(写入 Xcode CodeSnippets 目录)
7
+ function getSnippetsPath() {
8
+ const USER_HOME = process.env.HOME || process.env.USERPROFILE;
9
+ const snippetsPath = path.join(USER_HOME, 'Library/Developer/Xcode/UserData/CodeSnippets');
10
+
11
+ // 确保目录存在
12
+ try {
13
+ fs.accessSync(snippetsPath, fs.constants.F_OK);
14
+ } catch (err) {
15
+ fs.mkdirSync(snippetsPath, { recursive: true });
16
+ }
17
+
18
+ return snippetsPath;
19
+ }
20
+
21
+ // 获取缓存目录路径(使用用户 home 目录下的缓存)
22
+ function getCachePath() {
23
+ const USER_HOME = process.env.HOME || process.env.USERPROFILE;
24
+ const cachePath = path.join(USER_HOME, '.autosnippet', 'cache');
25
+
26
+ // 确保目录存在
27
+ try {
28
+ fs.accessSync(cachePath, fs.constants.F_OK);
29
+ } catch (err) {
30
+ fs.mkdirSync(cachePath, { recursive: true });
31
+ }
32
+
33
+ return cachePath;
34
+ }
35
+
36
+ module.exports = {
37
+ getSnippetsPath,
38
+ getCachePath
39
+ };
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
- function createCodeSnippets(specFile, answers, updateSnippet) {
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
- const answersKeys = answers.completion_first + answers.completion_more + answers.title;
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 + answers.completion_more + '@Moudle',
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
- const filePath = CMD_PATH;
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
- fs.readdir(filePath, function (err, files) {
85
- if (err) {
86
- console.log(err);
87
- return;
88
- }
89
-
90
- files.forEach(function (filename) {
91
- const filedir = path.join(filePath, filename);
92
- try {
93
- // 读取路径是否为文件
94
- const stats = fs.lstatSync(filedir);
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
- } catch (err) {
100
- console.error(err);
101
- }
102
- });
181
+ });
103
182
 
104
- readStream(specFile, filePathArr, snippet, isHaveHeader);
105
- });
183
+ readStream(specFile, filePathArr, snippet, isHaveHeader);
184
+ });
185
+ }
106
186
  }
107
187
 
108
188
  function readStream(specFile, filePathArr, snippet, isHaveHeader) {
@@ -149,27 +229,58 @@ function readStream(specFile, filePathArr, snippet, isHaveHeader) {
149
229
  const fileName = filePath.substring(slashIndex + 1, dotIndex + 1) + 'h';
150
230
  const thePath = filePath.substring(0, slashIndex + 1);
151
231
 
152
- const specSlashIndex = specFile.lastIndexOf('/');
153
- const specFilePath = specFile.substring(0, specSlashIndex + 1);
154
-
155
- findPath.findBPSpacPath(thePath, function (findFilePath, specName, readme) {
156
- const specPureName = specName.split('.')[0];
157
-
158
- findPath.findSubHeaderPath(findFilePath, specPureName).then(function (headerPath) {
232
+ // 使用 SPM 的 Package.swift 查找(替代 .boxspec)
233
+ findPath.findPackageSwiftPath(thePath).then(async function (packagePath) {
234
+ if (!packagePath) {
235
+ console.log('未找到 Package.swift 文件,请检查路径。');
159
236
  snippet['{content}'] = codeList;
160
- snippet['{specName}'] = specPureName;
161
- snippet['{headName}'] = fileName;
162
-
163
- if (headerPath) {
164
- snippet['{specHeadPath}'] = encodeURI(headerPath.replace(specFilePath, ''));
165
- }
237
+ saveFromFile(specFile, snippet);
238
+ return;
239
+ }
166
240
 
167
- if (readme) {
168
- const readmePath = path.join(findFilePath, readme).replace(specFilePath, '');
169
- snippet['{readme}'] = encodeURI(readmePath);
170
- }
241
+ // 解析 Package.swift 获取模块信息
242
+ const packageInfo = await findPath.parsePackageSwift(packagePath);
243
+ if (!packageInfo) {
244
+ snippet['{content}'] = codeList;
171
245
  saveFromFile(specFile, snippet);
172
- });
246
+ return;
247
+ }
248
+
249
+ // 根据当前文件路径确定模块名
250
+ const moduleName = determineModuleName(filePath, packageInfo);
251
+ const headerName = fileName.substring(0, fileName.length - 2); // 移除 .h
252
+
253
+ // ✅ 查找头文件(适配 SPM 的 include/ModuleName/ 结构)
254
+ const headerPath = await findPath.findSubHeaderPath(packageInfo.path, headerName, moduleName);
255
+
256
+ snippet['{content}'] = codeList;
257
+ snippet['{specName}'] = moduleName;
258
+ snippet['{headName}'] = fileName;
259
+
260
+ if (headerPath) {
261
+ // ✅ 使用 path.relative() 计算相对于配置文件的相对路径
262
+ const specFileDir = path.dirname(specFile);
263
+ const relativePath = path.relative(specFileDir, headerPath);
264
+ snippet['{specHeadPath}'] = encodeURI(relativePath);
265
+ }
266
+
267
+ // 查找 README.md
268
+ try {
269
+ const readmePath = path.join(packageInfo.path, README_NAME);
270
+ await fs.promises.access(readmePath);
271
+ // ✅ 使用 path.relative() 计算相对于配置文件的相对路径
272
+ const specFileDir = path.dirname(specFile);
273
+ const readmeRelativePath = path.relative(specFileDir, readmePath);
274
+ snippet['{readme}'] = encodeURI(readmeRelativePath);
275
+ } catch {
276
+ // README.md 不存在,跳过
277
+ }
278
+
279
+ saveFromFile(specFile, snippet);
280
+ }).catch(function (err) {
281
+ console.error('Error finding Package.swift:', err);
282
+ snippet['{content}'] = codeList;
283
+ saveFromFile(specFile, snippet);
173
284
  });
174
285
  } else {
175
286
  snippet['{content}'] = codeList;
@@ -222,7 +333,8 @@ function saveFromFile(specFile, snippet) {
222
333
  console.log(err);
223
334
  }
224
335
  cache.updateCache(specFile, content);
225
- install.addCodeSnippets(specFile);
336
+ // ✅ 只写入刚创建的代码片段,而不是所有代码片段
337
+ install.addCodeSnippets(specFile, snippet);
226
338
  }
227
339
  }
228
340
  }
@@ -258,4 +370,5 @@ function escapeString(string) {
258
370
 
259
371
  exports.createCodeSnippets = createCodeSnippets;
260
372
  exports.updateCodeSnippets = updateCodeSnippets;
261
- exports.saveFromFile = saveFromFile;
373
+ exports.saveFromFile = saveFromFile;
374
+ exports.findFilesWithACode = findFilesWithACode;