Skip to content

Commit 29ef201

Browse files
committed
Refactor saved context management to support merged loading and path resolution across multi-root workspaces
1 parent a368f76 commit 29ef201

9 files changed

Lines changed: 311 additions & 216 deletions

File tree

‎packages/vscode/src/commands/apply-context-command/apply-context-command.ts‎

Lines changed: 4 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ import {
1717
import {
1818
load_all_contexts,
1919
get_contexts_file_path,
20-
load_contexts_for_workspace
20+
load_and_merge_global_contexts
2121
} from './helpers/saving'
2222
import { handle_quick_save } from './helpers/saving/handle-quick-save'
2323

@@ -44,18 +44,9 @@ export const apply_context_command = (
4444
return
4545
}
4646

47-
const workspace_root = workspace_provider.get_workspace_roots()[0]
48-
if (!workspace_root) {
49-
vscode.window.showErrorMessage(
50-
dictionary.error_message.NO_WORKSPACE_ROOT
51-
)
52-
return
53-
}
54-
55-
const internal_contexts: SavedContext[] = load_contexts_for_workspace(
56-
extension_context,
57-
workspace_root
58-
)
47+
// Load merged contexts for display count
48+
const { merged: internal_contexts } =
49+
load_and_merge_global_contexts(extension_context)
5950

6051
const file_contexts_map = await load_all_contexts()
6152
let file_contexts_count = 0

‎packages/vscode/src/commands/apply-context-command/helpers/applying/apply-saved-context.ts‎

Lines changed: 4 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,56 +1,19 @@
11
import * as vscode from 'vscode'
2-
import * as path from 'path'
32
import { WorkspaceProvider } from '../../../../context/providers/workspace/workspace-provider'
43
import { LAST_CONTEXT_MERGE_REPLACE_OPTION_STATE_KEY } from '../../../../constants/state-keys'
54
import { SavedContext } from '@/types/context'
6-
import { Logger } from '@shared/utils/logger'
75
import { dictionary } from '@shared/constants/dictionary'
8-
import { resolve_glob_patterns } from './resolve-glob-patterns'
6+
import { resolve_context_paths } from './resolve-context-paths'
97

108
export const apply_saved_context = async (
119
context: SavedContext,
1210
workspace_root: string,
1311
workspace_provider: WorkspaceProvider,
1412
extension_context: vscode.ExtensionContext
1513
): Promise<void> => {
16-
const workspace_folders = vscode.workspace.workspaceFolders || []
17-
const workspace_map = new Map<string, string>()
18-
19-
for (const folder of workspace_folders) {
20-
workspace_map.set(folder.name, folder.uri.fsPath)
21-
}
22-
23-
const absolute_paths = context.paths.map((prefixed_path) => {
24-
const is_exclude = prefixed_path.startsWith('!')
25-
const path_part = is_exclude ? prefixed_path.substring(1) : prefixed_path
26-
27-
let resolved_path_part: string
28-
29-
if (path_part.includes(':')) {
30-
const [prefix, relative_path] = path_part.split(':', 2)
31-
32-
const root = workspace_map.get(prefix)
33-
34-
if (root) {
35-
resolved_path_part = path.join(root, relative_path)
36-
} else {
37-
Logger.warn({
38-
function_name: 'apply_saved_context',
39-
message: `Unknown workspace prefix "${prefix}" in path "${path_part}". Treating as relative to current workspace root.`
40-
})
41-
resolved_path_part = path.join(workspace_root, relative_path)
42-
}
43-
} else {
44-
resolved_path_part = path.isAbsolute(path_part)
45-
? path_part
46-
: path.join(workspace_root, path_part)
47-
}
48-
49-
return is_exclude ? `!${resolved_path_part}` : resolved_path_part
50-
})
51-
52-
const resolved_paths = await resolve_glob_patterns(
53-
absolute_paths,
14+
const resolved_paths = await resolve_context_paths(
15+
context,
16+
workspace_root,
5417
workspace_provider
5518
)
5619

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
export * from './apply-saved-context'
22
export * from './resolve-glob-patterns'
33
export * from './resolve-paths'
4+
export * from './resolve-context-paths'
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
import * as vscode from 'vscode'
2+
import * as path from 'path'
3+
import { SavedContext } from '@/types/context'
4+
import { WorkspaceProvider } from '@/context/providers/workspace/workspace-provider'
5+
import { Logger } from '@shared/utils/logger'
6+
import { resolve_glob_patterns } from './resolve-glob-patterns'
7+
8+
export const resolve_context_paths = async (
9+
context: SavedContext,
10+
workspace_root: string,
11+
workspace_provider: WorkspaceProvider
12+
): Promise<string[]> => {
13+
const workspace_folders = vscode.workspace.workspaceFolders || []
14+
const workspace_map = new Map<string, string>()
15+
16+
for (const folder of workspace_folders) {
17+
workspace_map.set(folder.name, folder.uri.fsPath)
18+
}
19+
20+
const absolute_paths = context.paths.map((prefixed_path) => {
21+
const is_exclude = prefixed_path.startsWith('!')
22+
const path_part = is_exclude ? prefixed_path.substring(1) : prefixed_path
23+
24+
let resolved_path_part: string
25+
26+
if (path_part.includes(':')) {
27+
const [prefix, relative_path] = path_part.split(':', 2)
28+
29+
const root = workspace_map.get(prefix)
30+
31+
if (root) {
32+
resolved_path_part = path.join(root, relative_path)
33+
} else {
34+
Logger.warn({
35+
function_name: 'resolve_context_paths',
36+
message: `Unknown workspace prefix "${prefix}" in path "${path_part}". Treating as relative to current workspace root.`
37+
})
38+
resolved_path_part = path.join(workspace_root, relative_path)
39+
}
40+
} else {
41+
resolved_path_part = path.isAbsolute(path_part)
42+
? path_part
43+
: path.join(workspace_root, path_part)
44+
}
45+
46+
return is_exclude ? `!${resolved_path_part}` : resolved_path_part
47+
})
48+
49+
return resolve_glob_patterns(absolute_paths, workspace_provider)
50+
}

‎packages/vscode/src/commands/apply-context-command/helpers/saving/global-storage-utils.ts‎

Lines changed: 45 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ const get_global_contexts_file_path = (
1313
return path.join(context.globalStorageUri.fsPath, GLOBAL_CONTEXTS_FILENAME)
1414
}
1515

16-
const load_all_global_contexts = (
16+
const load_all_global_contexts_data = (
1717
context: vscode.ExtensionContext
1818
): GlobalContextsData => {
1919
const file_path = get_global_contexts_file_path(context)
@@ -32,7 +32,7 @@ export const load_contexts_for_workspace = (
3232
context: vscode.ExtensionContext,
3333
workspace_root: string
3434
): SavedContext[] => {
35-
const all_data = load_all_global_contexts(context)
35+
const all_data = load_all_global_contexts_data(context)
3636
return all_data[workspace_root] || []
3737
}
3838

@@ -48,7 +48,7 @@ export const save_contexts_for_workspace = (
4848
fs.mkdirSync(dir_path, { recursive: true })
4949
}
5050

51-
const all_data = load_all_global_contexts(context)
51+
const all_data = load_all_global_contexts_data(context)
5252
all_data[workspace_root] = contexts
5353

5454
try {
@@ -58,3 +58,45 @@ export const save_contexts_for_workspace = (
5858
throw new Error('Failed to save contexts to global storage.')
5959
}
6060
}
61+
62+
export const load_and_merge_global_contexts = (
63+
context: vscode.ExtensionContext
64+
): { merged: SavedContext[]; context_to_roots: Map<string, string[]> } => {
65+
const workspace_folders = vscode.workspace.workspaceFolders || []
66+
const should_prefix = workspace_folders.length > 1
67+
const contexts_by_name = new Map<
68+
string,
69+
{ paths: string[]; roots: string[] }
70+
>()
71+
72+
for (const folder of workspace_folders) {
73+
const root = folder.uri.fsPath
74+
const contexts = load_contexts_for_workspace(context, root)
75+
76+
for (const c of contexts) {
77+
if (!contexts_by_name.has(c.name)) {
78+
contexts_by_name.set(c.name, { paths: [], roots: [] })
79+
}
80+
const entry = contexts_by_name.get(c.name)!
81+
82+
const paths_to_add = should_prefix
83+
? c.paths.map((p) => `${folder.name}:${p}`)
84+
: c.paths
85+
86+
entry.paths.push(...paths_to_add)
87+
entry.roots.push(root)
88+
}
89+
}
90+
91+
const merged: SavedContext[] = []
92+
const context_to_roots = new Map<string, string[]>()
93+
94+
for (const [name, data] of contexts_by_name.entries()) {
95+
merged.push({ name, paths: data.paths })
96+
context_to_roots.set(name, data.roots)
97+
}
98+
99+
merged.sort((a, b) => a.name.localeCompare(b.name))
100+
101+
return { merged, context_to_roots }
102+
}

‎packages/vscode/src/commands/apply-context-command/sources/json-file-source.ts‎

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,8 @@ export const load_and_merge_file_contexts = async (): Promise<{
5959
context_to_roots.set(name, data.roots)
6060
}
6161

62+
merged.sort((a, b) => a.name.localeCompare(b.name))
63+
6264
return { merged, context_to_roots }
6365
}
6466

@@ -108,13 +110,15 @@ export const handle_json_file_source = async (
108110
kind: vscode.QuickPickItemKind.Separator
109111
})
110112

113+
const is_multi_root = workspace_folders.length > 1
114+
111115
for (const context of file_contexts) {
112116
const roots = context_to_roots.get(context.name) || []
113117
let description = `${context.paths.length} path${
114118
context.paths.length === 1 ? '' : 's'
115119
}`
116120

117-
if (roots.length > 1) {
121+
if (roots.length > 0 && (roots.length > 1 || is_multi_root)) {
118122
const workspace_names = roots.map((root) => {
119123
const folder = workspace_folders.find(
120124
(f) => f.uri.fsPath === root

0 commit comments

Comments
 (0)