Skip to content

Commit 6dc6744

Browse files
committed
feat(projectFavorites): 改用全局用户目录存储配置
- 配置存储位置改为 ~/.fusi-tools/favorites/<project-hash>.json - Git 仓库使用 .git 目录路径 hash 作为项目标识 - Git Worktree 共享同一配置(通过 git-common-dir 识别) - 非 Git 项目使用 workspace 根目录路径 hash - 支持跨 IDE 同步配置,无需重新配置
1 parent f7771f6 commit 6dc6744

1 file changed

Lines changed: 109 additions & 15 deletions

File tree

src/features/projectFavorites/favoritesManager.ts

Lines changed: 109 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
import * as vscode from "vscode";
22
import * as path from "path";
3+
import * as fs from "fs";
4+
import * as os from "os";
5+
import * as crypto from "crypto";
36
import { Logger } from "../../logger";
47

58
// Helper for generating IDs if uuid is not available or too heavy
@@ -10,6 +13,11 @@ function generateId(): string {
1013
);
1114
}
1215

16+
// Helper for generating project key hash
17+
function hashPath(input: string): string {
18+
return crypto.createHash("md5").update(input).digest("hex").substring(0, 12);
19+
}
20+
1321
export interface FavoriteFile {
1422
id: string;
1523
path: string; // stored relative if possible
@@ -32,38 +40,41 @@ interface FavoriteData {
3240
categories: FavoriteCategory[];
3341
}
3442

35-
const STORAGE_KEY = "fusi-tools.projectFavorites.data";
43+
const STORAGE_DIR = path.join(os.homedir(), ".fusi-tools", "favorites");
3644

3745
export class FavoritesManager {
3846
private _data: FavoriteData;
3947
private _context: vscode.ExtensionContext;
48+
private _storagePath: string | null = null;
4049
private _onDidChangeTreeData: vscode.EventEmitter<
4150
void | FavoriteCategory | undefined
4251
> = new vscode.EventEmitter<void | FavoriteCategory | undefined>();
4352
readonly onDidChangeTreeData: vscode.Event<
4453
void | FavoriteCategory | undefined
4554
> = this._onDidChangeTreeData.event;
46-
55+
4756
// 文件存在性缓存
4857
private _fileExistenceCache: Map<string, { exists: boolean; timestamp: number }> = new Map();
4958
private _cacheExpirationMs: number = 30000; // 30秒缓存过期时间
5059
private _checkTimer: NodeJS.Timeout | undefined;
5160

5261
constructor(context: vscode.ExtensionContext) {
5362
this._context = context;
54-
this._data = this._context.workspaceState.get<FavoriteData>(STORAGE_KEY, {
55-
files: {},
56-
categories: [
57-
{
58-
id: "default",
59-
name: "默认分类",
60-
expanded: true,
61-
fileIds: [],
62-
},
63-
],
64-
});
63+
this._storagePath = this.getStoragePath();
6564

66-
// Ensure data integrity (in case of corruption or fresh start)
65+
if (this._storagePath && fs.existsSync(this._storagePath)) {
66+
try {
67+
const content = fs.readFileSync(this._storagePath, "utf-8");
68+
this._data = JSON.parse(content);
69+
} catch (e) {
70+
Logger.warn("读取配置文件失败,使用默认配置", e);
71+
this._data = this.getDefaultData();
72+
}
73+
} else {
74+
this._data = this.getDefaultData();
75+
}
76+
77+
// Ensure data integrity
6778
if (!this._data.categories || this._data.categories.length === 0) {
6879
this._data.categories = [
6980
{
@@ -82,8 +93,91 @@ export class FavoritesManager {
8293
this.startPeriodicFileCheck();
8394
}
8495

96+
private getDefaultData(): FavoriteData {
97+
return {
98+
files: {},
99+
categories: [
100+
{
101+
id: "default",
102+
name: "默认分类",
103+
expanded: true,
104+
fileIds: [],
105+
},
106+
],
107+
};
108+
}
109+
110+
/**
111+
* 获取项目唯一标识
112+
* Git 仓库使用 git-common-dir 路径 hash(支持 worktree)
113+
* 非 Git 仓库使用 workspace 根目录路径 hash
114+
*/
115+
private getProjectKey(): string | null {
116+
const workspaceFolders = vscode.workspace.workspaceFolders;
117+
if (!workspaceFolders || workspaceFolders.length === 0) {
118+
return null;
119+
}
120+
121+
const rootPath = workspaceFolders[0].uri.fsPath;
122+
const gitDir = path.join(rootPath, ".git");
123+
124+
// 检查是否是 Git 仓库
125+
if (fs.existsSync(gitDir)) {
126+
try {
127+
// 如果 .git 是文件(worktree 情况),读取其内容获取真正的 git 目录
128+
const gitStat = fs.statSync(gitDir);
129+
if (gitStat.isFile()) {
130+
const gitContent = fs.readFileSync(gitDir, "utf-8");
131+
// 格式: gitdir: /path/to/.git/worktrees/name
132+
const match = gitContent.match(/gitdir:\s*(.+)/);
133+
if (match) {
134+
// 获取 git common dir (主仓库的 .git 目录)
135+
const worktreeGitDir = match[1].trim();
136+
// worktree 的 gitdir 通常在 .git/worktrees/xxx 下
137+
// common dir 是 worktreeGitDir 的父目录的父目录
138+
const commonDir = path.dirname(path.dirname(worktreeGitDir));
139+
return hashPath(commonDir);
140+
}
141+
} else if (gitStat.isDirectory()) {
142+
// 普通仓库,直接使用 .git 目录路径
143+
return hashPath(gitDir);
144+
}
145+
} catch (e) {
146+
Logger.warn("获取 Git 目录失败", e);
147+
}
148+
}
149+
150+
// 非 Git 仓库,使用 workspace 根目录
151+
return hashPath(rootPath);
152+
}
153+
154+
/**
155+
* 获取存储文件路径
156+
*/
157+
private getStoragePath(): string | null {
158+
const projectKey = this.getProjectKey();
159+
if (!projectKey) {
160+
return null;
161+
}
162+
return path.join(STORAGE_DIR, `${projectKey}.json`);
163+
}
164+
85165
private saveData() {
86-
this._context.workspaceState.update(STORAGE_KEY, this._data);
166+
if (!this._storagePath) {
167+
// 没有工作区时,回退到 workspaceState
168+
this._context.workspaceState.update("fusi-tools.projectFavorites.data", this._data);
169+
} else {
170+
try {
171+
// 确保目录存在
172+
const dir = path.dirname(this._storagePath);
173+
if (!fs.existsSync(dir)) {
174+
fs.mkdirSync(dir, { recursive: true });
175+
}
176+
fs.writeFileSync(this._storagePath, JSON.stringify(this._data, null, 2), "utf-8");
177+
} catch (e) {
178+
Logger.error("保存配置文件失败", e);
179+
}
180+
}
87181
this._onDidChangeTreeData.fire();
88182
}
89183

0 commit comments

Comments
 (0)