autosnippet 3.2.21 → 3.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/dashboard/dist/assets/{icons-C1dUryS-.js → icons-BofcEZ3f.js} +1 -1
- package/dashboard/dist/assets/index-SiN1GChm.js +128 -0
- package/dashboard/dist/index.html +2 -2
- package/dist/bin/cli.d.ts +0 -1
- package/dist/bin/cli.js +0 -133
- package/dist/lib/cli/SetupService.d.ts +46 -2
- package/dist/lib/cli/SetupService.js +2 -27
- package/dist/lib/{platform/ios/spm → core/discovery}/SpmDiscoverer.d.ts +2 -5
- package/dist/lib/{platform/ios/spm → core/discovery}/SpmDiscoverer.js +159 -44
- package/dist/lib/core/discovery/index.d.ts +1 -1
- package/dist/lib/core/discovery/index.js +2 -2
- package/dist/lib/external/mcp/handlers/guard.js +6 -3
- package/dist/lib/http/HttpServer.js +0 -6
- package/dist/lib/http/routes/commands.d.ts +1 -1
- package/dist/lib/http/routes/commands.js +1 -66
- package/dist/lib/http/routes/remote.js +0 -5
- package/dist/lib/injection/ServiceMap.d.ts +0 -9
- package/dist/lib/injection/modules/AppModule.d.ts +2 -3
- package/dist/lib/injection/modules/AppModule.js +3 -30
- package/dist/lib/injection/modules/GuardModule.js +33 -1
- package/dist/lib/service/guard/GuardCheckEngine.d.ts +13 -1
- package/dist/lib/service/guard/GuardCheckEngine.js +44 -2
- package/dist/lib/service/module/ModuleService.js +3 -13
- package/dist/lib/service/search/SearchEngine.js +1 -1
- package/dist/lib/shared/constants.d.ts +0 -15
- package/dist/lib/shared/constants.js +0 -10
- package/dist/lib/shared/schemas/config.d.ts +4 -1
- package/dist/lib/shared/schemas/config.js +8 -1
- package/dist/scripts/release.js +2 -10
- package/package.json +4 -19
- package/dashboard/dist/assets/index-DdvZE4Yd.js +0 -128
- package/dist/lib/http/routes/snippets.d.ts +0 -6
- package/dist/lib/http/routes/snippets.js +0 -49
- package/dist/lib/platform/ClipboardManager.d.ts +0 -24
- package/dist/lib/platform/ClipboardManager.js +0 -142
- package/dist/lib/platform/NativeUi.d.ts +0 -53
- package/dist/lib/platform/NativeUi.js +0 -284
- package/dist/lib/platform/ios/index.d.ts +0 -38
- package/dist/lib/platform/ios/index.js +0 -42
- package/dist/lib/platform/ios/routes/spm.d.ts +0 -9
- package/dist/lib/platform/ios/routes/spm.js +0 -371
- package/dist/lib/platform/ios/snippet/PlaceholderConverter.d.ts +0 -21
- package/dist/lib/platform/ios/snippet/PlaceholderConverter.js +0 -48
- package/dist/lib/platform/ios/snippet/XcodeCodec.d.ts +0 -23
- package/dist/lib/platform/ios/snippet/XcodeCodec.js +0 -96
- package/dist/lib/platform/ios/spm/DependencyGraph.d.ts +0 -56
- package/dist/lib/platform/ios/spm/DependencyGraph.js +0 -195
- package/dist/lib/platform/ios/spm/PackageSwiftParser.d.ts +0 -69
- package/dist/lib/platform/ios/spm/PackageSwiftParser.js +0 -231
- package/dist/lib/platform/ios/spm/PathFinder.d.ts +0 -28
- package/dist/lib/platform/ios/spm/PathFinder.js +0 -117
- package/dist/lib/platform/ios/spm/PolicyEngine.d.ts +0 -44
- package/dist/lib/platform/ios/spm/PolicyEngine.js +0 -79
- package/dist/lib/platform/ios/spm/SpmHelper.d.ts +0 -102
- package/dist/lib/platform/ios/spm/SpmHelper.js +0 -464
- package/dist/lib/platform/ios/xcode/HeaderResolver.d.ts +0 -33
- package/dist/lib/platform/ios/xcode/HeaderResolver.js +0 -90
- package/dist/lib/platform/ios/xcode/SaveEventFilter.d.ts +0 -66
- package/dist/lib/platform/ios/xcode/SaveEventFilter.js +0 -142
- package/dist/lib/platform/ios/xcode/XcodeAutomation.d.ts +0 -71
- package/dist/lib/platform/ios/xcode/XcodeAutomation.js +0 -327
- package/dist/lib/platform/ios/xcode/XcodeImportResolver.d.ts +0 -130
- package/dist/lib/platform/ios/xcode/XcodeImportResolver.js +0 -404
- package/dist/lib/platform/ios/xcode/XcodeIntegration.d.ts +0 -89
- package/dist/lib/platform/ios/xcode/XcodeIntegration.js +0 -588
- package/dist/lib/platform/ios/xcode/XcodeWriteUtils.d.ts +0 -99
- package/dist/lib/platform/ios/xcode/XcodeWriteUtils.js +0 -190
- package/dist/lib/service/automation/ActionPipeline.d.ts +0 -34
- package/dist/lib/service/automation/ActionPipeline.js +0 -53
- package/dist/lib/service/automation/AutomationOrchestrator.d.ts +0 -86
- package/dist/lib/service/automation/AutomationOrchestrator.js +0 -57
- package/dist/lib/service/automation/ContextCollector.d.ts +0 -24
- package/dist/lib/service/automation/ContextCollector.js +0 -35
- package/dist/lib/service/automation/DirectiveDetector.d.ts +0 -51
- package/dist/lib/service/automation/DirectiveDetector.js +0 -112
- package/dist/lib/service/automation/FileWatcher.d.ts +0 -51
- package/dist/lib/service/automation/FileWatcher.js +0 -366
- package/dist/lib/service/automation/TriggerResolver.d.ts +0 -36
- package/dist/lib/service/automation/TriggerResolver.js +0 -62
- package/dist/lib/service/automation/handlers/AlinkHandler.d.ts +0 -7
- package/dist/lib/service/automation/handlers/AlinkHandler.js +0 -80
- package/dist/lib/service/automation/handlers/CreateHandler.d.ts +0 -11
- package/dist/lib/service/automation/handlers/CreateHandler.js +0 -170
- package/dist/lib/service/automation/handlers/GuardHandler.d.ts +0 -17
- package/dist/lib/service/automation/handlers/GuardHandler.js +0 -218
- package/dist/lib/service/automation/handlers/HeaderHandler.d.ts +0 -2
- package/dist/lib/service/automation/handlers/HeaderHandler.js +0 -32
- package/dist/lib/service/automation/handlers/SearchHandler.d.ts +0 -11
- package/dist/lib/service/automation/handlers/SearchHandler.js +0 -278
- package/dist/lib/service/snippet/SnippetFactory.d.ts +0 -101
- package/dist/lib/service/snippet/SnippetFactory.js +0 -145
- package/dist/lib/service/snippet/SnippetInstaller.d.ts +0 -91
- package/dist/lib/service/snippet/SnippetInstaller.js +0 -276
- package/dist/lib/service/snippet/codecs/SnippetCodec.d.ts +0 -44
- package/dist/lib/service/snippet/codecs/SnippetCodec.js +0 -35
- package/dist/lib/service/snippet/codecs/VSCodeCodec.d.ts +0 -27
- package/dist/lib/service/snippet/codecs/VSCodeCodec.js +0 -82
- package/dist/scripts/build-native-ui.d.ts +0 -3
- package/dist/scripts/build-native-ui.js +0 -62
- package/dist/scripts/init-snippets.d.ts +0 -30
- package/dist/scripts/init-snippets.js +0 -298
- package/dist/scripts/install-full.d.ts +0 -7
- package/dist/scripts/install-full.js +0 -38
- package/resources/native-ui/README.md +0 -29
- package/resources/native-ui/combined-window.swift +0 -494
- package/resources/native-ui/main.swift +0 -598
- package/scripts/postinstall-safe.mjs +0 -89
|
@@ -1,90 +0,0 @@
|
|
|
1
|
-
import fs from 'node:fs';
|
|
2
|
-
import path from 'node:path';
|
|
3
|
-
import * as PathFinder from '../spm/PathFinder.js';
|
|
4
|
-
/**
|
|
5
|
-
* HeaderResolver — ObjC/Swift #import 头文件路径解析
|
|
6
|
-
* 解析 import 语句,定位头文件相对路径
|
|
7
|
-
*/
|
|
8
|
-
/** 解析单行 import 语句 */
|
|
9
|
-
export function parseImportLine(headerStr) {
|
|
10
|
-
const s = String(headerStr || '').trim();
|
|
11
|
-
const angle = s.match(/#import\s+<([^>]+)>/);
|
|
12
|
-
if (angle) {
|
|
13
|
-
const part = angle[1].trim();
|
|
14
|
-
const slash = part.indexOf('/');
|
|
15
|
-
if (slash !== -1) {
|
|
16
|
-
return { moduleName: part.slice(0, slash), headerName: path.basename(part, '.h') };
|
|
17
|
-
}
|
|
18
|
-
return { moduleName: null, headerName: path.basename(part, '.h') };
|
|
19
|
-
}
|
|
20
|
-
const quote = s.match(/#import\s+"([^"]+)"/);
|
|
21
|
-
if (quote) {
|
|
22
|
-
return { moduleName: null, headerName: path.basename(quote[1], '.h') };
|
|
23
|
-
}
|
|
24
|
-
return null;
|
|
25
|
-
}
|
|
26
|
-
/** 解析单个 import 的相对路径 */
|
|
27
|
-
export async function resolveHeaderRelativePath(headerStr, targetRootDir) {
|
|
28
|
-
if (!targetRootDir) {
|
|
29
|
-
return undefined;
|
|
30
|
-
}
|
|
31
|
-
const parsed = parseImportLine(headerStr);
|
|
32
|
-
if (!parsed) {
|
|
33
|
-
return undefined;
|
|
34
|
-
}
|
|
35
|
-
const { moduleName, headerName } = parsed;
|
|
36
|
-
const headerPath = await PathFinder.findSubHeaderPath(targetRootDir, headerName, moduleName);
|
|
37
|
-
if (!headerPath) {
|
|
38
|
-
return undefined;
|
|
39
|
-
}
|
|
40
|
-
return path.relative(targetRootDir, headerPath);
|
|
41
|
-
}
|
|
42
|
-
/** 从文件路径和 Package 信息推断模块名 */
|
|
43
|
-
export function determineModuleNameFromPath(filePath, packageInfo) {
|
|
44
|
-
const relativePath = path.relative(packageInfo.path, filePath);
|
|
45
|
-
const segments = relativePath.split(path.sep);
|
|
46
|
-
for (let i = segments.length - 1; i >= 0; i--) {
|
|
47
|
-
if (packageInfo.targets?.includes(segments[i])) {
|
|
48
|
-
return segments[i];
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
return packageInfo.targets?.[0] || packageInfo.name || null;
|
|
52
|
-
}
|
|
53
|
-
/** 综合解析:从路径+文本提取 headers、headerPaths、moduleName */
|
|
54
|
-
export async function resolveHeadersForText(projectRoot, relativePath, text) {
|
|
55
|
-
const fullPath = path.resolve(projectRoot, (relativePath || '').trim());
|
|
56
|
-
if (!relativePath || !fs.existsSync(fullPath)) {
|
|
57
|
-
return { headers: [], headerPaths: [], moduleName: null };
|
|
58
|
-
}
|
|
59
|
-
const targetRootDir = await PathFinder.findTargetRootDir(fullPath);
|
|
60
|
-
let moduleName = targetRootDir ? path.basename(targetRootDir) : null;
|
|
61
|
-
const importRegex = /^(?:#import|import)\s+.*$/gm;
|
|
62
|
-
let headers = (text.match(importRegex) || []).filter(Boolean);
|
|
63
|
-
let headerPaths = targetRootDir
|
|
64
|
-
? await Promise.all(headers.map((h) => resolveHeaderRelativePath(h, targetRootDir)))
|
|
65
|
-
: [];
|
|
66
|
-
// 如果 .m/.mm 无 import,尝试推断同名 .h
|
|
67
|
-
const ext = path.extname(fullPath).toLowerCase();
|
|
68
|
-
if (targetRootDir && headers.length === 0 && (ext === '.m' || ext === '.mm')) {
|
|
69
|
-
const headerNameWithoutExt = path.basename(fullPath, ext);
|
|
70
|
-
const packagePath = await PathFinder.findPackageSwiftPath(fullPath);
|
|
71
|
-
const packageInfo = packagePath ? await PathFinder.parsePackageSwift(packagePath) : null;
|
|
72
|
-
if (packageInfo) {
|
|
73
|
-
moduleName = determineModuleNameFromPath(fullPath, packageInfo);
|
|
74
|
-
}
|
|
75
|
-
const headerPath = await PathFinder.findSubHeaderPath(targetRootDir, headerNameWithoutExt, moduleName);
|
|
76
|
-
if (headerPath) {
|
|
77
|
-
headers = [
|
|
78
|
-
`#import <${moduleName || path.basename(targetRootDir)}/${headerNameWithoutExt}.h>`,
|
|
79
|
-
];
|
|
80
|
-
headerPaths = [path.relative(targetRootDir, headerPath)];
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
return { headers, headerPaths, moduleName };
|
|
84
|
-
}
|
|
85
|
-
export default {
|
|
86
|
-
resolveHeadersForText,
|
|
87
|
-
parseImportLine,
|
|
88
|
-
resolveHeaderRelativePath,
|
|
89
|
-
determineModuleNameFromPath,
|
|
90
|
-
};
|
|
@@ -1,66 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* SaveEventFilter - 区分用户保存与 Xcode 自动保存
|
|
3
|
-
*
|
|
4
|
-
* 三层过滤策略:
|
|
5
|
-
* 1. Self-write 冷却期:AutoSnippet 自身写入的文件在冷却期内忽略变更
|
|
6
|
-
* 2. 内容哈希去重:文件内容未变时跳过处理
|
|
7
|
-
* 3. Xcode 焦点检测:Xcode 不是前台应用时跳过(典型的切窗口自动保存场景)
|
|
8
|
-
*
|
|
9
|
-
* 使用方式:
|
|
10
|
-
* import { saveEventFilter } from './SaveEventFilter.js';
|
|
11
|
-
*
|
|
12
|
-
* // 在 handler 写文件时标记
|
|
13
|
-
* saveEventFilter.markSelfWrite(filePath);
|
|
14
|
-
* fs.writeFileSync(filePath, content);
|
|
15
|
-
*
|
|
16
|
-
* // 在 FileWatcher 处理前判断
|
|
17
|
-
* const verdict = saveEventFilter.shouldProcess(filePath, content);
|
|
18
|
-
* if (!verdict.process) { console.log(verdict.reason); return; }
|
|
19
|
-
*/
|
|
20
|
-
declare class SaveEventFilter {
|
|
21
|
-
_cleanupInterval: ReturnType<typeof setInterval>;
|
|
22
|
-
_contentHashes: Map<string, string>;
|
|
23
|
-
_selfWrites: Map<string, number>;
|
|
24
|
-
constructor();
|
|
25
|
-
/**
|
|
26
|
-
* 标记 AutoSnippet 即将写入某个文件
|
|
27
|
-
* 在 fs.writeFile / writeFileSync 之前调用本方法
|
|
28
|
-
*
|
|
29
|
-
* @param filePath 绝对路径
|
|
30
|
-
*/
|
|
31
|
-
markSelfWrite(filePath: string): void;
|
|
32
|
-
/**
|
|
33
|
-
* 判断是否应该处理该文件的变更事件
|
|
34
|
-
*
|
|
35
|
-
* @param filePath 绝对路径
|
|
36
|
-
* @param content 文件当前的完整内容
|
|
37
|
-
* @returns }
|
|
38
|
-
*/
|
|
39
|
-
shouldProcess(filePath: string, content: string): {
|
|
40
|
-
process: boolean;
|
|
41
|
-
reason: string;
|
|
42
|
-
};
|
|
43
|
-
/**
|
|
44
|
-
* 处理完成后更新内容哈希
|
|
45
|
-
* 在文件指令处理完毕后调用
|
|
46
|
-
*
|
|
47
|
-
* @param filePath 绝对路径
|
|
48
|
-
* @param content 处理后的文件内容
|
|
49
|
-
*/
|
|
50
|
-
updateHash(filePath: string, content: string): void;
|
|
51
|
-
/**
|
|
52
|
-
* 在 handler 写文件之前同时标记 self-write + 更新哈希
|
|
53
|
-
* 方便一步完成
|
|
54
|
-
*
|
|
55
|
-
* @param filePath 绝对路径
|
|
56
|
-
* @param newContent 即将写入的新内容
|
|
57
|
-
*/
|
|
58
|
-
markWrite(filePath: string, newContent: string): void;
|
|
59
|
-
/** 清除某文件的所有状态(通常不需要调用) */
|
|
60
|
-
clear(filePath: string): void;
|
|
61
|
-
_hash(content: string): string;
|
|
62
|
-
_cleanup(): void;
|
|
63
|
-
}
|
|
64
|
-
/** 单例 */
|
|
65
|
-
export declare const saveEventFilter: SaveEventFilter;
|
|
66
|
-
export default saveEventFilter;
|
|
@@ -1,142 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* SaveEventFilter - 区分用户保存与 Xcode 自动保存
|
|
3
|
-
*
|
|
4
|
-
* 三层过滤策略:
|
|
5
|
-
* 1. Self-write 冷却期:AutoSnippet 自身写入的文件在冷却期内忽略变更
|
|
6
|
-
* 2. 内容哈希去重:文件内容未变时跳过处理
|
|
7
|
-
* 3. Xcode 焦点检测:Xcode 不是前台应用时跳过(典型的切窗口自动保存场景)
|
|
8
|
-
*
|
|
9
|
-
* 使用方式:
|
|
10
|
-
* import { saveEventFilter } from './SaveEventFilter.js';
|
|
11
|
-
*
|
|
12
|
-
* // 在 handler 写文件时标记
|
|
13
|
-
* saveEventFilter.markSelfWrite(filePath);
|
|
14
|
-
* fs.writeFileSync(filePath, content);
|
|
15
|
-
*
|
|
16
|
-
* // 在 FileWatcher 处理前判断
|
|
17
|
-
* const verdict = saveEventFilter.shouldProcess(filePath, content);
|
|
18
|
-
* if (!verdict.process) { console.log(verdict.reason); return; }
|
|
19
|
-
*/
|
|
20
|
-
import { createHash } from 'node:crypto';
|
|
21
|
-
import { isXcodeFrontmost } from './XcodeAutomation.js';
|
|
22
|
-
/* ────────── 配置 ────────── */
|
|
23
|
-
/** Self-write 冷却期(ms) */
|
|
24
|
-
const SELF_WRITE_COOLDOWN = 2000;
|
|
25
|
-
/** 是否启用 Xcode 焦点过滤(可通过 ASD_SAVE_FILTER=0 关闭) */
|
|
26
|
-
function isFilterEnabled() {
|
|
27
|
-
return process.env.ASD_SAVE_FILTER !== '0';
|
|
28
|
-
}
|
|
29
|
-
/** 是否启用 Xcode 焦点检测(可通过 ASD_XCODE_FOCUS_CHECK=0 单独关闭) */
|
|
30
|
-
function isFocusCheckEnabled() {
|
|
31
|
-
return process.env.ASD_XCODE_FOCUS_CHECK !== '0';
|
|
32
|
-
}
|
|
33
|
-
/* ────────── SaveEventFilter ────────── */
|
|
34
|
-
class SaveEventFilter {
|
|
35
|
-
_cleanupInterval;
|
|
36
|
-
_contentHashes;
|
|
37
|
-
_selfWrites;
|
|
38
|
-
constructor() {
|
|
39
|
-
/** filePath → 最后一次 self-write 的时间戳 */
|
|
40
|
-
this._selfWrites = new Map();
|
|
41
|
-
/** filePath → 上次处理时的内容 MD5 哈希 */
|
|
42
|
-
this._contentHashes = new Map();
|
|
43
|
-
// 定期清理过期条目,防止内存泄漏
|
|
44
|
-
this._cleanupInterval = setInterval(() => this._cleanup(), 60_000);
|
|
45
|
-
if (this._cleanupInterval.unref) {
|
|
46
|
-
this._cleanupInterval.unref();
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
/**
|
|
50
|
-
* 标记 AutoSnippet 即将写入某个文件
|
|
51
|
-
* 在 fs.writeFile / writeFileSync 之前调用本方法
|
|
52
|
-
*
|
|
53
|
-
* @param filePath 绝对路径
|
|
54
|
-
*/
|
|
55
|
-
markSelfWrite(filePath) {
|
|
56
|
-
this._selfWrites.set(filePath, Date.now());
|
|
57
|
-
}
|
|
58
|
-
/**
|
|
59
|
-
* 判断是否应该处理该文件的变更事件
|
|
60
|
-
*
|
|
61
|
-
* @param filePath 绝对路径
|
|
62
|
-
* @param content 文件当前的完整内容
|
|
63
|
-
* @returns }
|
|
64
|
-
*/
|
|
65
|
-
shouldProcess(filePath, content) {
|
|
66
|
-
if (!isFilterEnabled()) {
|
|
67
|
-
return { process: true, reason: 'filter-disabled' };
|
|
68
|
-
}
|
|
69
|
-
// ── Layer 1: Self-write 冷却期 ──
|
|
70
|
-
const lastSelfWrite = this._selfWrites.get(filePath);
|
|
71
|
-
if (lastSelfWrite && Date.now() - lastSelfWrite < SELF_WRITE_COOLDOWN) {
|
|
72
|
-
if (process.env.ASD_DEBUG === '1') {
|
|
73
|
-
}
|
|
74
|
-
return { process: false, reason: 'self-write-cooldown' };
|
|
75
|
-
}
|
|
76
|
-
// ── Layer 2: 内容哈希去重 ──
|
|
77
|
-
const hash = this._hash(content);
|
|
78
|
-
const prevHash = this._contentHashes.get(filePath);
|
|
79
|
-
if (prevHash && prevHash === hash) {
|
|
80
|
-
if (process.env.ASD_DEBUG === '1') {
|
|
81
|
-
}
|
|
82
|
-
return { process: false, reason: 'content-unchanged' };
|
|
83
|
-
}
|
|
84
|
-
// ── Layer 3: Xcode 焦点检测 ──
|
|
85
|
-
if (process.platform === 'darwin' && isFocusCheckEnabled()) {
|
|
86
|
-
if (!isXcodeFrontmost()) {
|
|
87
|
-
if (process.env.ASD_DEBUG === '1') {
|
|
88
|
-
}
|
|
89
|
-
return { process: false, reason: 'xcode-not-frontmost' };
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
return { process: true, reason: 'ok' };
|
|
93
|
-
}
|
|
94
|
-
/**
|
|
95
|
-
* 处理完成后更新内容哈希
|
|
96
|
-
* 在文件指令处理完毕后调用
|
|
97
|
-
*
|
|
98
|
-
* @param filePath 绝对路径
|
|
99
|
-
* @param content 处理后的文件内容
|
|
100
|
-
*/
|
|
101
|
-
updateHash(filePath, content) {
|
|
102
|
-
this._contentHashes.set(filePath, this._hash(content));
|
|
103
|
-
}
|
|
104
|
-
/**
|
|
105
|
-
* 在 handler 写文件之前同时标记 self-write + 更新哈希
|
|
106
|
-
* 方便一步完成
|
|
107
|
-
*
|
|
108
|
-
* @param filePath 绝对路径
|
|
109
|
-
* @param newContent 即将写入的新内容
|
|
110
|
-
*/
|
|
111
|
-
markWrite(filePath, newContent) {
|
|
112
|
-
this.markSelfWrite(filePath);
|
|
113
|
-
this._contentHashes.set(filePath, this._hash(newContent));
|
|
114
|
-
}
|
|
115
|
-
/** 清除某文件的所有状态(通常不需要调用) */
|
|
116
|
-
clear(filePath) {
|
|
117
|
-
this._selfWrites.delete(filePath);
|
|
118
|
-
this._contentHashes.delete(filePath);
|
|
119
|
-
}
|
|
120
|
-
/* ────────── 内部方法 ────────── */
|
|
121
|
-
_hash(content) {
|
|
122
|
-
return createHash('md5').update(content).digest('hex');
|
|
123
|
-
}
|
|
124
|
-
_cleanup() {
|
|
125
|
-
const now = Date.now();
|
|
126
|
-
const expiry = 10 * 60 * 1000; // 10 分钟
|
|
127
|
-
for (const [k, ts] of this._selfWrites) {
|
|
128
|
-
if (now - ts > expiry) {
|
|
129
|
-
this._selfWrites.delete(k);
|
|
130
|
-
}
|
|
131
|
-
}
|
|
132
|
-
// 内容哈希保留更长时间,只清理明显过期的
|
|
133
|
-
// (文件仍被监听期间不应清理,所以这里保守处理)
|
|
134
|
-
if (this._contentHashes.size > 1000) {
|
|
135
|
-
// 超过 1000 个文件说明有泄漏,全部清理
|
|
136
|
-
this._contentHashes.clear();
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
/** 单例 */
|
|
141
|
-
export const saveEventFilter = new SaveEventFilter();
|
|
142
|
-
export default saveEventFilter;
|
|
@@ -1,71 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* XcodeAutomation — Xcode AppleScript 自动化
|
|
3
|
-
*
|
|
4
|
-
* 通过 osascript 向 Xcode 发送键盘事件,实现行级操作:
|
|
5
|
-
* 跳转行、选中行内容、剪切行、粘贴、在行首插入、删除行内容、保存文档。
|
|
6
|
-
*
|
|
7
|
-
* 所有操作都带超时保护(OSASCRIPT_TIMEOUT),Xcode 未运行时安全跳过。
|
|
8
|
-
* 仅支持 macOS。
|
|
9
|
-
*/
|
|
10
|
-
/** 检查 Xcode 是否正在运行(不会启动 Xcode) */
|
|
11
|
-
export declare function isXcodeRunning(): boolean;
|
|
12
|
-
/** 检查 Xcode 是否为当前前台应用 */
|
|
13
|
-
export declare function isXcodeFrontmost(): boolean;
|
|
14
|
-
/**
|
|
15
|
-
* 跳转到指定行
|
|
16
|
-
*
|
|
17
|
-
* 按键序列:Cmd+L → 输入行号 → Return
|
|
18
|
-
*
|
|
19
|
-
* @param lineNumber 1-based 行号
|
|
20
|
-
* @returns 是否成功
|
|
21
|
-
*/
|
|
22
|
-
export declare function jumpToLineInXcode(lineNumber: number): boolean;
|
|
23
|
-
/**
|
|
24
|
-
* 剪切指定行的文本内容(不含换行符)
|
|
25
|
-
*
|
|
26
|
-
* 按键序列:Cmd+L 跳转 → Cmd+← 行首 → Cmd+Shift+→ 选到行尾 → Cmd+X 剪切
|
|
27
|
-
*
|
|
28
|
-
* @param lineNumber 1-based 行号
|
|
29
|
-
* @returns 是否成功
|
|
30
|
-
*/
|
|
31
|
-
export declare function cutLineInXcode(lineNumber: number): boolean;
|
|
32
|
-
/**
|
|
33
|
-
* 删除指定行的文本内容(保留空行,不删除行本身)
|
|
34
|
-
*
|
|
35
|
-
* 按键序列:Cmd+L 跳转 → Cmd+← 行首 → Cmd+Shift+→ 选到行尾 → Delete
|
|
36
|
-
*
|
|
37
|
-
* @param lineNumber 1-based 行号
|
|
38
|
-
* @returns 是否成功
|
|
39
|
-
*/
|
|
40
|
-
export declare function deleteLineContentInXcode(lineNumber: number): boolean;
|
|
41
|
-
/**
|
|
42
|
-
* 执行粘贴(Cmd+V)
|
|
43
|
-
*
|
|
44
|
-
* 调用前须确保剪贴板已写入目标内容。
|
|
45
|
-
* @returns 是否成功
|
|
46
|
-
*/
|
|
47
|
-
export declare function pasteInXcode(): boolean;
|
|
48
|
-
/**
|
|
49
|
-
* 选中当前行内容后粘贴替换
|
|
50
|
-
*
|
|
51
|
-
* 假设光标已在目标行(通常由 jumpToLineInXcode 定位后调用)。
|
|
52
|
-
* 按键序列:Cmd+← 行首 → Cmd+Shift+→ 选到行尾 → Cmd+V 粘贴替换
|
|
53
|
-
*
|
|
54
|
-
* @returns 是否成功
|
|
55
|
-
*/
|
|
56
|
-
export declare function selectAndPasteInXcode(): boolean;
|
|
57
|
-
/**
|
|
58
|
-
* 跳转到指定行行首并粘贴剪贴板内容
|
|
59
|
-
*
|
|
60
|
-
* 用于在 import 区域插入新行。
|
|
61
|
-
* 按键序列:Cmd+L → 输入行号 → Return → Cmd+← 行首 → Cmd+V 粘贴
|
|
62
|
-
*
|
|
63
|
-
* @param lineNumber 1-based 行号
|
|
64
|
-
* @returns 是否成功
|
|
65
|
-
*/
|
|
66
|
-
export declare function insertAtLineStartInXcode(lineNumber: number): boolean;
|
|
67
|
-
/**
|
|
68
|
-
* 保存 Xcode 当前活动文档(Cmd+S)
|
|
69
|
-
* @returns 是否成功
|
|
70
|
-
*/
|
|
71
|
-
export declare function saveActiveDocumentInXcode(): boolean;
|
|
@@ -1,327 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* XcodeAutomation — Xcode AppleScript 自动化
|
|
3
|
-
*
|
|
4
|
-
* 通过 osascript 向 Xcode 发送键盘事件,实现行级操作:
|
|
5
|
-
* 跳转行、选中行内容、剪切行、粘贴、在行首插入、删除行内容、保存文档。
|
|
6
|
-
*
|
|
7
|
-
* 所有操作都带超时保护(OSASCRIPT_TIMEOUT),Xcode 未运行时安全跳过。
|
|
8
|
-
* 仅支持 macOS。
|
|
9
|
-
*/
|
|
10
|
-
import { execSync, spawnSync } from 'node:child_process';
|
|
11
|
-
const OSASCRIPT_TIMEOUT = 5000;
|
|
12
|
-
// ─────────────────────────────────────────────
|
|
13
|
-
// 内部辅助
|
|
14
|
-
// ─────────────────────────────────────────────
|
|
15
|
-
/**
|
|
16
|
-
* 将行号限制为有效正整数(最小值 1)
|
|
17
|
-
* @param n 原始行号
|
|
18
|
-
* @returns 安全的 1-based 行号
|
|
19
|
-
*/
|
|
20
|
-
function _safeLine(n) {
|
|
21
|
-
return Number.isFinite(n) && n > 0 ? n : 1;
|
|
22
|
-
}
|
|
23
|
-
/**
|
|
24
|
-
* 执行 osascript 并返回是否成功
|
|
25
|
-
* @param args osascript 参数数组(每对 `-e`, `script`)
|
|
26
|
-
*/
|
|
27
|
-
function _run(args) {
|
|
28
|
-
try {
|
|
29
|
-
const res = spawnSync('osascript', args, { stdio: 'ignore', timeout: OSASCRIPT_TIMEOUT });
|
|
30
|
-
return res.status === 0;
|
|
31
|
-
}
|
|
32
|
-
catch {
|
|
33
|
-
return false;
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
// ─────────────────────────────────────────────
|
|
37
|
-
// 状态查询
|
|
38
|
-
// ─────────────────────────────────────────────
|
|
39
|
-
/** 检查 Xcode 是否正在运行(不会启动 Xcode) */
|
|
40
|
-
export function isXcodeRunning() {
|
|
41
|
-
if (process.platform !== 'darwin') {
|
|
42
|
-
return false;
|
|
43
|
-
}
|
|
44
|
-
try {
|
|
45
|
-
const result = execSync('pgrep -x Xcode', {
|
|
46
|
-
encoding: 'utf8',
|
|
47
|
-
timeout: 2000,
|
|
48
|
-
stdio: 'pipe',
|
|
49
|
-
});
|
|
50
|
-
return result.trim().length > 0;
|
|
51
|
-
}
|
|
52
|
-
catch {
|
|
53
|
-
return false;
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
/** 检查 Xcode 是否为当前前台应用 */
|
|
57
|
-
export function isXcodeFrontmost() {
|
|
58
|
-
if (!isXcodeRunning()) {
|
|
59
|
-
return false;
|
|
60
|
-
}
|
|
61
|
-
try {
|
|
62
|
-
const result = execSync('osascript -e \'tell application "System Events" to get name of first process whose frontmost is true\'', { encoding: 'utf8', timeout: OSASCRIPT_TIMEOUT, stdio: 'pipe' });
|
|
63
|
-
return result.trim() === 'Xcode';
|
|
64
|
-
}
|
|
65
|
-
catch {
|
|
66
|
-
return false;
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
// ─────────────────────────────────────────────
|
|
70
|
-
// 行操作
|
|
71
|
-
// ─────────────────────────────────────────────
|
|
72
|
-
/**
|
|
73
|
-
* 跳转到指定行
|
|
74
|
-
*
|
|
75
|
-
* 按键序列:Cmd+L → 输入行号 → Return
|
|
76
|
-
*
|
|
77
|
-
* @param lineNumber 1-based 行号
|
|
78
|
-
* @returns 是否成功
|
|
79
|
-
*/
|
|
80
|
-
export function jumpToLineInXcode(lineNumber) {
|
|
81
|
-
if (!isXcodeRunning()) {
|
|
82
|
-
return false;
|
|
83
|
-
}
|
|
84
|
-
const n = _safeLine(lineNumber);
|
|
85
|
-
return _run([
|
|
86
|
-
'-e',
|
|
87
|
-
'tell application "Xcode" to activate',
|
|
88
|
-
'-e',
|
|
89
|
-
'delay 0.2',
|
|
90
|
-
'-e',
|
|
91
|
-
'tell application "System Events"',
|
|
92
|
-
'-e',
|
|
93
|
-
' keystroke "l" using command down',
|
|
94
|
-
'-e',
|
|
95
|
-
' delay 0.2',
|
|
96
|
-
'-e',
|
|
97
|
-
` keystroke "${String(n)}"`,
|
|
98
|
-
'-e',
|
|
99
|
-
' delay 0.2',
|
|
100
|
-
'-e',
|
|
101
|
-
' key code 36',
|
|
102
|
-
'-e',
|
|
103
|
-
'end tell',
|
|
104
|
-
]);
|
|
105
|
-
}
|
|
106
|
-
/**
|
|
107
|
-
* 剪切指定行的文本内容(不含换行符)
|
|
108
|
-
*
|
|
109
|
-
* 按键序列:Cmd+L 跳转 → Cmd+← 行首 → Cmd+Shift+→ 选到行尾 → Cmd+X 剪切
|
|
110
|
-
*
|
|
111
|
-
* @param lineNumber 1-based 行号
|
|
112
|
-
* @returns 是否成功
|
|
113
|
-
*/
|
|
114
|
-
export function cutLineInXcode(lineNumber) {
|
|
115
|
-
if (!isXcodeRunning()) {
|
|
116
|
-
return false;
|
|
117
|
-
}
|
|
118
|
-
const n = _safeLine(lineNumber);
|
|
119
|
-
return _run([
|
|
120
|
-
'-e',
|
|
121
|
-
'tell application "Xcode" to activate',
|
|
122
|
-
'-e',
|
|
123
|
-
'delay 0.5',
|
|
124
|
-
'-e',
|
|
125
|
-
'tell application "System Events"',
|
|
126
|
-
'-e',
|
|
127
|
-
' keystroke "l" using command down', // Cmd+L: Go to Line
|
|
128
|
-
'-e',
|
|
129
|
-
' delay 0.5',
|
|
130
|
-
'-e',
|
|
131
|
-
` keystroke "${String(n)}"`, // 输入行号
|
|
132
|
-
'-e',
|
|
133
|
-
' delay 0.5',
|
|
134
|
-
'-e',
|
|
135
|
-
' key code 36', // Return
|
|
136
|
-
'-e',
|
|
137
|
-
' delay 0.5',
|
|
138
|
-
'-e',
|
|
139
|
-
' key code 123 using command down', // Cmd+← 行首
|
|
140
|
-
'-e',
|
|
141
|
-
' delay 0.5',
|
|
142
|
-
'-e',
|
|
143
|
-
' key code 124 using {command down, shift down}', // Cmd+Shift+→ 选到行尾
|
|
144
|
-
'-e',
|
|
145
|
-
' delay 0.5',
|
|
146
|
-
'-e',
|
|
147
|
-
' keystroke "x" using command down', // Cmd+X
|
|
148
|
-
'-e',
|
|
149
|
-
'end tell',
|
|
150
|
-
]);
|
|
151
|
-
}
|
|
152
|
-
/**
|
|
153
|
-
* 删除指定行的文本内容(保留空行,不删除行本身)
|
|
154
|
-
*
|
|
155
|
-
* 按键序列:Cmd+L 跳转 → Cmd+← 行首 → Cmd+Shift+→ 选到行尾 → Delete
|
|
156
|
-
*
|
|
157
|
-
* @param lineNumber 1-based 行号
|
|
158
|
-
* @returns 是否成功
|
|
159
|
-
*/
|
|
160
|
-
export function deleteLineContentInXcode(lineNumber) {
|
|
161
|
-
if (!isXcodeRunning()) {
|
|
162
|
-
return false;
|
|
163
|
-
}
|
|
164
|
-
const n = _safeLine(lineNumber);
|
|
165
|
-
return _run([
|
|
166
|
-
'-e',
|
|
167
|
-
'tell application "Xcode" to activate',
|
|
168
|
-
'-e',
|
|
169
|
-
'delay 0.3',
|
|
170
|
-
'-e',
|
|
171
|
-
'tell application "System Events"',
|
|
172
|
-
'-e',
|
|
173
|
-
' keystroke "l" using command down',
|
|
174
|
-
'-e',
|
|
175
|
-
' delay 0.3',
|
|
176
|
-
'-e',
|
|
177
|
-
` keystroke "${String(n)}"`,
|
|
178
|
-
'-e',
|
|
179
|
-
' delay 0.3',
|
|
180
|
-
'-e',
|
|
181
|
-
' key code 36',
|
|
182
|
-
'-e',
|
|
183
|
-
' delay 0.3',
|
|
184
|
-
'-e',
|
|
185
|
-
' key code 123 using command down', // Cmd+← 行首
|
|
186
|
-
'-e',
|
|
187
|
-
' delay 0.2',
|
|
188
|
-
'-e',
|
|
189
|
-
' key code 124 using {command down, shift down}', // Cmd+Shift+→ 选到行尾
|
|
190
|
-
'-e',
|
|
191
|
-
' delay 0.2',
|
|
192
|
-
'-e',
|
|
193
|
-
' key code 51', // Delete 键
|
|
194
|
-
'-e',
|
|
195
|
-
' delay 0.3',
|
|
196
|
-
'-e',
|
|
197
|
-
'end tell',
|
|
198
|
-
]);
|
|
199
|
-
}
|
|
200
|
-
// ─────────────────────────────────────────────
|
|
201
|
-
// 粘贴操作
|
|
202
|
-
// ─────────────────────────────────────────────
|
|
203
|
-
/**
|
|
204
|
-
* 执行粘贴(Cmd+V)
|
|
205
|
-
*
|
|
206
|
-
* 调用前须确保剪贴板已写入目标内容。
|
|
207
|
-
* @returns 是否成功
|
|
208
|
-
*/
|
|
209
|
-
export function pasteInXcode() {
|
|
210
|
-
if (!isXcodeRunning()) {
|
|
211
|
-
return false;
|
|
212
|
-
}
|
|
213
|
-
return _run([
|
|
214
|
-
'-e',
|
|
215
|
-
'tell application "Xcode" to activate',
|
|
216
|
-
'-e',
|
|
217
|
-
'delay 0.2',
|
|
218
|
-
'-e',
|
|
219
|
-
'tell application "System Events"',
|
|
220
|
-
'-e',
|
|
221
|
-
' keystroke "v" using command down',
|
|
222
|
-
'-e',
|
|
223
|
-
'end tell',
|
|
224
|
-
]);
|
|
225
|
-
}
|
|
226
|
-
/**
|
|
227
|
-
* 选中当前行内容后粘贴替换
|
|
228
|
-
*
|
|
229
|
-
* 假设光标已在目标行(通常由 jumpToLineInXcode 定位后调用)。
|
|
230
|
-
* 按键序列:Cmd+← 行首 → Cmd+Shift+→ 选到行尾 → Cmd+V 粘贴替换
|
|
231
|
-
*
|
|
232
|
-
* @returns 是否成功
|
|
233
|
-
*/
|
|
234
|
-
export function selectAndPasteInXcode() {
|
|
235
|
-
if (!isXcodeRunning()) {
|
|
236
|
-
return false;
|
|
237
|
-
}
|
|
238
|
-
return _run([
|
|
239
|
-
'-e',
|
|
240
|
-
'tell application "Xcode" to activate',
|
|
241
|
-
'-e',
|
|
242
|
-
'delay 0.5',
|
|
243
|
-
'-e',
|
|
244
|
-
'tell application "System Events"',
|
|
245
|
-
'-e',
|
|
246
|
-
' key code 123 using command down', // Cmd+← 行首
|
|
247
|
-
'-e',
|
|
248
|
-
' delay 0.1',
|
|
249
|
-
'-e',
|
|
250
|
-
' key code 124 using {command down, shift down}', // Cmd+Shift+→ 选到行尾
|
|
251
|
-
'-e',
|
|
252
|
-
' delay 0.2',
|
|
253
|
-
'-e',
|
|
254
|
-
' keystroke "v" using command down', // Cmd+V 粘贴替换
|
|
255
|
-
'-e',
|
|
256
|
-
'end tell',
|
|
257
|
-
]);
|
|
258
|
-
}
|
|
259
|
-
/**
|
|
260
|
-
* 跳转到指定行行首并粘贴剪贴板内容
|
|
261
|
-
*
|
|
262
|
-
* 用于在 import 区域插入新行。
|
|
263
|
-
* 按键序列:Cmd+L → 输入行号 → Return → Cmd+← 行首 → Cmd+V 粘贴
|
|
264
|
-
*
|
|
265
|
-
* @param lineNumber 1-based 行号
|
|
266
|
-
* @returns 是否成功
|
|
267
|
-
*/
|
|
268
|
-
export function insertAtLineStartInXcode(lineNumber) {
|
|
269
|
-
if (!isXcodeRunning()) {
|
|
270
|
-
return false;
|
|
271
|
-
}
|
|
272
|
-
const n = _safeLine(lineNumber);
|
|
273
|
-
return _run([
|
|
274
|
-
'-e',
|
|
275
|
-
'tell application "Xcode" to activate',
|
|
276
|
-
'-e',
|
|
277
|
-
'delay 0.3',
|
|
278
|
-
'-e',
|
|
279
|
-
'tell application "System Events"',
|
|
280
|
-
'-e',
|
|
281
|
-
' keystroke "l" using command down', // Cmd+L: Go to Line
|
|
282
|
-
'-e',
|
|
283
|
-
' delay 0.3',
|
|
284
|
-
'-e',
|
|
285
|
-
` keystroke "${String(n)}"`, // 输入行号
|
|
286
|
-
'-e',
|
|
287
|
-
' delay 0.3',
|
|
288
|
-
'-e',
|
|
289
|
-
' key code 36', // Return
|
|
290
|
-
'-e',
|
|
291
|
-
' delay 0.3',
|
|
292
|
-
'-e',
|
|
293
|
-
' key code 123 using command down', // Cmd+← 行首
|
|
294
|
-
'-e',
|
|
295
|
-
' delay 0.2',
|
|
296
|
-
'-e',
|
|
297
|
-
' keystroke "v" using command down', // Cmd+V 粘贴
|
|
298
|
-
'-e',
|
|
299
|
-
' delay 0.3',
|
|
300
|
-
'-e',
|
|
301
|
-
'end tell',
|
|
302
|
-
]);
|
|
303
|
-
}
|
|
304
|
-
// ─────────────────────────────────────────────
|
|
305
|
-
// 文档操作
|
|
306
|
-
// ─────────────────────────────────────────────
|
|
307
|
-
/**
|
|
308
|
-
* 保存 Xcode 当前活动文档(Cmd+S)
|
|
309
|
-
* @returns 是否成功
|
|
310
|
-
*/
|
|
311
|
-
export function saveActiveDocumentInXcode() {
|
|
312
|
-
if (!isXcodeRunning()) {
|
|
313
|
-
return false;
|
|
314
|
-
}
|
|
315
|
-
return _run([
|
|
316
|
-
'-e',
|
|
317
|
-
'tell application "Xcode" to activate',
|
|
318
|
-
'-e',
|
|
319
|
-
'delay 0.1',
|
|
320
|
-
'-e',
|
|
321
|
-
'tell application "System Events"',
|
|
322
|
-
'-e',
|
|
323
|
-
' keystroke "s" using command down',
|
|
324
|
-
'-e',
|
|
325
|
-
'end tell',
|
|
326
|
-
]);
|
|
327
|
-
}
|