From 9a97701183e6f34da321cbd3bc6a8c61b2a15f40 Mon Sep 17 00:00:00 2001 From: Addison Date: Thu, 13 Aug 2026 07:43:09 -0400 Subject: [PATCH 1/9] fix: resolve brightscript.bsdk correctly when .code-workspace is not at project root VSCode's getConfiguration pre-resolves relative paths and variables like ${workspaceFolder} against the wrong base when the .code-workspace file is not at the project root. Use inspect().workspaceValue to get the raw unresolved string, then expand ${workspaceFolder} and ${workspaceFolder:name} variables ourselves before passing to parseVersionInfo. Co-Authored-By: Claude Sonnet 4.6 --- src/LanguageServerManager.ts | 35 ++++++++++++++++++++++++++++++++++- 1 file changed, 34 insertions(+), 1 deletion(-) diff --git a/src/LanguageServerManager.ts b/src/LanguageServerManager.ts index b426d5ea..4212f3fc 100644 --- a/src/LanguageServerManager.ts +++ b/src/LanguageServerManager.ts @@ -524,7 +524,7 @@ export class LanguageServerManager { //use bsdk entry in the code-workspace file if (this.workspaceConfigIncludesBsdkKey()) { let result = this.parseVersionInfo( - util.getConfiguration('brightscript', vscode.workspace.workspaceFile).get('bsdk')?.trim?.(), + this.workspaceBsdkPath(), path.dirname(vscode.workspace.workspaceFile.fsPath) ); if (result) { @@ -557,6 +557,39 @@ export class LanguageServerManager { } } + /** + * Get the `brightscript.bsdk` value from the .code-workspace settings block with workspace + * folder variables expanded. `inspect().workspaceValue` returns the unresolved string from + * the .code-workspace file — VSCode does not expand variables like ${workspaceFolder} in this + * value, so we expand ${workspaceFolder} and ${workspaceFolder:name} ourselves. + */ + private workspaceBsdkPath(): string { + const rawValue = util.getConfiguration('brightscript', vscode.workspace.workspaceFile).inspect('bsdk')?.workspaceValue?.trim?.(); + return this.expandWorkspaceBsdkPath(rawValue); + } + + /** + * Expand ${workspaceFolder} and ${workspaceFolder:name} variables in a bsdk path string. + * VSCode does not expand these variables in inspect().workspaceValue, so we do it ourselves. + * Throws if an unrecognized variable is encountered. + */ + private expandWorkspaceBsdkPath(value: string): string { + return value?.replace(/\$\{([^}]+)\}/g, (_match, expression) => { + const namedFolder = /^workspaceFolder:(.+)$/.exec(expression); + if (namedFolder) { + const folder = vscode.workspace.workspaceFolders?.find(f => f.name === namedFolder[ 1 ]); + if (!folder) { + throw new Error(`brightscript.bsdk: unknown workspace folder name "${namedFolder[ 1 ]}"`); + } + return folder.uri.fsPath; + } + if (expression === 'workspaceFolder') { + return vscode.workspace.workspaceFolders?.[ 0 ]?.uri.fsPath ?? _match; + } + throw new Error(`brightscript.bsdk: unsupported variable "\${${expression}}"`); + }); + } + private workspaceConfigIncludesBsdkKey() { return vscode.workspace.workspaceFile && fsExtra.pathExistsSync(vscode.workspace.workspaceFile.fsPath) && From bb6bc68a6edf9cf3281a8e042c01f46ce06f3c19 Mon Sep 17 00:00:00 2001 From: Addison Date: Thu, 13 Aug 2026 07:53:09 -0400 Subject: [PATCH 2/9] chore: fix linter spacing errors in expandWorkspaceBsdkPath Co-Authored-By: Claude Sonnet 4.6 --- src/LanguageServerManager.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/LanguageServerManager.ts b/src/LanguageServerManager.ts index 4212f3fc..18623ea2 100644 --- a/src/LanguageServerManager.ts +++ b/src/LanguageServerManager.ts @@ -577,14 +577,14 @@ export class LanguageServerManager { return value?.replace(/\$\{([^}]+)\}/g, (_match, expression) => { const namedFolder = /^workspaceFolder:(.+)$/.exec(expression); if (namedFolder) { - const folder = vscode.workspace.workspaceFolders?.find(f => f.name === namedFolder[ 1 ]); + const folder = vscode.workspace.workspaceFolders?.find(f => f.name === namedFolder[1]); if (!folder) { - throw new Error(`brightscript.bsdk: unknown workspace folder name "${namedFolder[ 1 ]}"`); + throw new Error(`brightscript.bsdk: unknown workspace folder name "${namedFolder[1]}"`); } return folder.uri.fsPath; } if (expression === 'workspaceFolder') { - return vscode.workspace.workspaceFolders?.[ 0 ]?.uri.fsPath ?? _match; + return vscode.workspace.workspaceFolders?.[0]?.uri.fsPath ?? _match; } throw new Error(`brightscript.bsdk: unsupported variable "\${${expression}}"`); }); From cc425b1095dea4deff49edd4f6017e763f8545e7 Mon Sep 17 00:00:00 2001 From: Addison Date: Thu, 13 Aug 2026 07:57:40 -0400 Subject: [PATCH 3/9] test: add coverage for bsdk path resolution from nested .code-workspace files Co-Authored-By: Claude Sonnet 4.6 --- src/LanguageServerManager.spec.ts | 91 +++++++++++++++++++++++++++++++ src/mockVscode.spec.ts | 3 +- 2 files changed, 93 insertions(+), 1 deletion(-) diff --git a/src/LanguageServerManager.spec.ts b/src/LanguageServerManager.spec.ts index 61734784..1ffbda72 100644 --- a/src/LanguageServerManager.spec.ts +++ b/src/LanguageServerManager.spec.ts @@ -270,6 +270,97 @@ describe('LanguageServerManager', () => { ); }); + it('resolves relative bsdk path from the workspace file directory, not the project root', async () => { + // .code-workspace file lives in a subdirectory (.vscode/) + vscode.workspace.workspaceFile = URI.file(s`${tempDir}/.vscode/workspace.code-workspace`); + vscode.workspace.workspaceFolders.push({ + index: 0, + name: 'SDK', + uri: URI.file(s`${tempDir}`) + }); + + setConfig(vscode.workspace.workspaceFile.fsPath, { + 'brightscript.bsdk': '../node_modules/brighterscript' + }); + + expect( + s(await languageServerManager['getBsdkVersionInfo']()) + ).to.eql( + // resolves from .vscode/ → one level up → tempDir/node_modules/brighterscript + s`${tempDir}/node_modules/brighterscript` + ); + }); + + it('expands ${workspaceFolder:name} variable in bsdk path', async () => { + vscode.workspace.workspaceFile = URI.file(s`${tempDir}/.vscode/workspace.code-workspace`); + vscode.workspace.workspaceFolders.push({ + index: 0, + name: 'SDK', + uri: URI.file(s`${tempDir}`) + }); + + setConfig(vscode.workspace.workspaceFile.fsPath, { + 'brightscript.bsdk': '${workspaceFolder:SDK}/node_modules/brighterscript' + }); + + expect( + s(await languageServerManager['getBsdkVersionInfo']()) + ).to.eql(s`${tempDir}/node_modules/brighterscript`); + }); + + it('expands ${workspaceFolder} variable to the first workspace folder', async () => { + vscode.workspace.workspaceFile = URI.file(s`${tempDir}/.vscode/workspace.code-workspace`); + vscode.workspace.workspaceFolders.push({ + index: 0, + name: 'SDK', + uri: URI.file(s`${tempDir}`) + }); + + setConfig(vscode.workspace.workspaceFile.fsPath, { + 'brightscript.bsdk': '${workspaceFolder}/node_modules/brighterscript' + }); + + expect( + s(await languageServerManager['getBsdkVersionInfo']()) + ).to.eql(s`${tempDir}/node_modules/brighterscript`); + }); + + it('throws for an unrecognized variable in bsdk path', async () => { + vscode.workspace.workspaceFile = URI.file(s`${tempDir}/.vscode/workspace.code-workspace`); + vscode.workspace.workspaceFolders.push({ + index: 0, + name: 'SDK', + uri: URI.file(s`${tempDir}`) + }); + + setConfig(vscode.workspace.workspaceFile.fsPath, { + 'brightscript.bsdk': '${env:MY_VAR}/node_modules/brighterscript' + }); + + await expectThrowsAsync( + () => languageServerManager['getBsdkVersionInfo'](), + 'brightscript.bsdk: unsupported variable "${env:MY_VAR}"' + ); + }); + + it('throws for an unknown workspace folder name in bsdk path', async () => { + vscode.workspace.workspaceFile = URI.file(s`${tempDir}/.vscode/workspace.code-workspace`); + vscode.workspace.workspaceFolders.push({ + index: 0, + name: 'SDK', + uri: URI.file(s`${tempDir}`) + }); + + setConfig(vscode.workspace.workspaceFile.fsPath, { + 'brightscript.bsdk': '${workspaceFolder:Unknown}/node_modules/brighterscript' + }); + + await expectThrowsAsync( + () => languageServerManager['getBsdkVersionInfo'](), + 'brightscript.bsdk: unknown workspace folder name "Unknown"' + ); + }); + it('returns folder version when not in a workspace', async () => { vscode.workspace.workspaceFolders.push({ index: 0, diff --git a/src/mockVscode.spec.ts b/src/mockVscode.spec.ts index ca9d89de..da421b09 100644 --- a/src/mockVscode.spec.ts +++ b/src/mockVscode.spec.ts @@ -191,7 +191,8 @@ export let vscode = { inspect: (name: string) => { return { key: name, - globalValue: store?.[`${configurationName}.${name}`] + globalValue: store?.[`${configurationName}.${name}`], + workspaceValue: store?.[`${configurationName}.${name}`] } as ReturnType; }, update: (name: string, value: any) => { From 016395f84cb758f016f466c795f04340acbe76c3 Mon Sep 17 00:00:00 2001 From: Addison Date: Thu, 13 Aug 2026 10:32:27 -0400 Subject: [PATCH 4/9] fix: bugs --- src/LanguageServerManager.ts | 58 +++++++++++++++++++++++------------- 1 file changed, 37 insertions(+), 21 deletions(-) diff --git a/src/LanguageServerManager.ts b/src/LanguageServerManager.ts index 18623ea2..0624bfe8 100644 --- a/src/LanguageServerManager.ts +++ b/src/LanguageServerManager.ts @@ -524,9 +524,10 @@ export class LanguageServerManager { //use bsdk entry in the code-workspace file if (this.workspaceConfigIncludesBsdkKey()) { let result = this.parseVersionInfo( - this.workspaceBsdkPath(), - path.dirname(vscode.workspace.workspaceFile.fsPath) + this.getWorkspaceBsdkInfo(vscode.workspace.workspaceFile), + path.dirname(vscode.workspace.workspaceFile.fsPath), ); + if (result) { return result.value; } @@ -534,7 +535,8 @@ export class LanguageServerManager { //collect `brightscript.bsdk` setting value from each workspaceFolder const folderResults = vscode.workspace.workspaceFolders?.reduce((acc, workspaceFolder) => { - const versionInfo = util.getConfiguration('brightscript', workspaceFolder).get('bsdk'); + const versionInfo = this.getWorkspaceBsdkInfo(vscode.workspace.workspaceFile) + const parsed = this.parseVersionInfo(versionInfo, workspaceFolder.uri.fsPath); if (parsed) { acc.set(parsed.value, parsed); @@ -563,9 +565,15 @@ export class LanguageServerManager { * the .code-workspace file — VSCode does not expand variables like ${workspaceFolder} in this * value, so we expand ${workspaceFolder} and ${workspaceFolder:name} ourselves. */ - private workspaceBsdkPath(): string { - const rawValue = util.getConfiguration('brightscript', vscode.workspace.workspaceFile).inspect('bsdk')?.workspaceValue?.trim?.(); - return this.expandWorkspaceBsdkPath(rawValue); + private getWorkspaceBsdkInfo(workspaceFolder: vscode.ConfigurationScope) { + const rawValue = util.getConfiguration('brightscript', workspaceFolder).inspect('bsdk')?.workspaceValue?.trim?.(); + + const hasVariable = /^\$\{/.test(rawValue); + if (!hasVariable) { + return rawValue; + } + + return this.expandWorkspaceBsdkInfo(rawValue); } /** @@ -573,21 +581,29 @@ export class LanguageServerManager { * VSCode does not expand these variables in inspect().workspaceValue, so we do it ourselves. * Throws if an unrecognized variable is encountered. */ - private expandWorkspaceBsdkPath(value: string): string { - return value?.replace(/\$\{([^}]+)\}/g, (_match, expression) => { - const namedFolder = /^workspaceFolder:(.+)$/.exec(expression); - if (namedFolder) { - const folder = vscode.workspace.workspaceFolders?.find(f => f.name === namedFolder[1]); - if (!folder) { - throw new Error(`brightscript.bsdk: unknown workspace folder name "${namedFolder[1]}"`); - } - return folder.uri.fsPath; - } - if (expression === 'workspaceFolder') { - return vscode.workspace.workspaceFolders?.[0]?.uri.fsPath ?? _match; - } - throw new Error(`brightscript.bsdk: unsupported variable "\${${expression}}"`); - }); + private expandWorkspaceBsdkInfo(value: string): string { + if (!value) { return value; } + + const [ match, workspaceName, relativePath ] = /^\$\{workspaceFolder:?([^}]*)\}(.*)$/.exec(value) ?? []; + + if (!match) { + throw new Error(`brightscript.bsdk: unsupported variable in bsdk "${value}"`); + } + + // if ${workspaceFolder}, use workspaceFolders[0] + let workspaceFolder = vscode.workspace.workspaceFolders?.[ 0 ] + + // if ${workspaceFolder:name}, find by name + if (workspaceName) { + workspaceFolder = vscode.workspace.workspaceFolders?.find(f => f.name === workspaceName); + } + + if (!workspaceFolder) { + throw new Error(`brightscript.bsdk: unknown workspace folder name "${workspaceName}"`); + } + + const bsdkInfo = path.join(workspaceFolder.uri.fsPath, relativePath) + return bsdkInfo ?? ''; } private workspaceConfigIncludesBsdkKey() { From ba21cc028aee4402cf9eb932cc1ddfca1dfa3839 Mon Sep 17 00:00:00 2001 From: Addison Date: Thu, 13 Aug 2026 10:40:04 -0400 Subject: [PATCH 5/9] update comment --- src/LanguageServerManager.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/LanguageServerManager.spec.ts b/src/LanguageServerManager.spec.ts index 1ffbda72..d43d825d 100644 --- a/src/LanguageServerManager.spec.ts +++ b/src/LanguageServerManager.spec.ts @@ -339,7 +339,7 @@ describe('LanguageServerManager', () => { await expectThrowsAsync( () => languageServerManager['getBsdkVersionInfo'](), - 'brightscript.bsdk: unsupported variable "${env:MY_VAR}"' + 'brightscript.bsdk: unsupported variable in bsdk "${env:MY_VAR}/node_modules/brighterscript"' ); }); From 1302889d4d5f8cc588434ad578120d6cd348caf0 Mon Sep 17 00:00:00 2001 From: Addison Date: Thu, 13 Aug 2026 11:49:49 -0400 Subject: [PATCH 6/9] fix linter warnings --- src/LanguageServerManager.ts | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/src/LanguageServerManager.ts b/src/LanguageServerManager.ts index 0624bfe8..0cbf7c87 100644 --- a/src/LanguageServerManager.ts +++ b/src/LanguageServerManager.ts @@ -525,7 +525,7 @@ export class LanguageServerManager { if (this.workspaceConfigIncludesBsdkKey()) { let result = this.parseVersionInfo( this.getWorkspaceBsdkInfo(vscode.workspace.workspaceFile), - path.dirname(vscode.workspace.workspaceFile.fsPath), + path.dirname(vscode.workspace.workspaceFile.fsPath) ); if (result) { @@ -535,8 +535,7 @@ export class LanguageServerManager { //collect `brightscript.bsdk` setting value from each workspaceFolder const folderResults = vscode.workspace.workspaceFolders?.reduce((acc, workspaceFolder) => { - const versionInfo = this.getWorkspaceBsdkInfo(vscode.workspace.workspaceFile) - + const versionInfo = this.getWorkspaceBsdkInfo(vscode.workspace.workspaceFile); const parsed = this.parseVersionInfo(versionInfo, workspaceFolder.uri.fsPath); if (parsed) { acc.set(parsed.value, parsed); @@ -568,7 +567,7 @@ export class LanguageServerManager { private getWorkspaceBsdkInfo(workspaceFolder: vscode.ConfigurationScope) { const rawValue = util.getConfiguration('brightscript', workspaceFolder).inspect('bsdk')?.workspaceValue?.trim?.(); - const hasVariable = /^\$\{/.test(rawValue); + const hasVariable = rawValue?.startsWith('${'); if (!hasVariable) { return rawValue; } @@ -582,16 +581,18 @@ export class LanguageServerManager { * Throws if an unrecognized variable is encountered. */ private expandWorkspaceBsdkInfo(value: string): string { - if (!value) { return value; } + if (!value) { + return value; + } - const [ match, workspaceName, relativePath ] = /^\$\{workspaceFolder:?([^}]*)\}(.*)$/.exec(value) ?? []; + const [match, workspaceName, relativePath] = /^\$\{workspaceFolder:?([^}]*)\}(.*)$/.exec(value) ?? []; if (!match) { throw new Error(`brightscript.bsdk: unsupported variable in bsdk "${value}"`); } // if ${workspaceFolder}, use workspaceFolders[0] - let workspaceFolder = vscode.workspace.workspaceFolders?.[ 0 ] + let workspaceFolder = vscode.workspace.workspaceFolders?.[0] // if ${workspaceFolder:name}, find by name if (workspaceName) { From bc9e2ae79003c21290d4f81693defba5e8eb086a Mon Sep 17 00:00:00 2001 From: Addison <152139745+addison-adler@users.noreply.github.com> Date: Sun, 16 Aug 2026 20:18:05 -0400 Subject: [PATCH 7/9] linter fixes Co-authored-by: Addison <152139745+addison-adler@users.noreply.github.com> --- src/LanguageServerManager.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/LanguageServerManager.ts b/src/LanguageServerManager.ts index 0cbf7c87..f168abae 100644 --- a/src/LanguageServerManager.ts +++ b/src/LanguageServerManager.ts @@ -592,7 +592,7 @@ export class LanguageServerManager { } // if ${workspaceFolder}, use workspaceFolders[0] - let workspaceFolder = vscode.workspace.workspaceFolders?.[0] + let workspaceFolder = vscode.workspace.workspaceFolders?.[0]; // if ${workspaceFolder:name}, find by name if (workspaceName) { @@ -603,7 +603,7 @@ export class LanguageServerManager { throw new Error(`brightscript.bsdk: unknown workspace folder name "${workspaceName}"`); } - const bsdkInfo = path.join(workspaceFolder.uri.fsPath, relativePath) + const bsdkInfo = path.join(workspaceFolder.uri.fsPath, relativePath); return bsdkInfo ?? ''; } From d4ed812e55f804a594a975f07eaeb2c23acfd58f Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 18 Aug 2026 08:16:56 +0000 Subject: [PATCH 8/9] fix: restore per-folder bsdk config lookup in getBsdkVersionInfo reduce loop The PR accidentally changed the per-folder config read to always fetch from vscode.workspace.workspaceFile instead of each workspaceFolder. This meant multi-folder workspaces never accumulated distinct bsdk values, so selectBrighterScriptVersion was never called. --- src/LanguageServerManager.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/LanguageServerManager.ts b/src/LanguageServerManager.ts index f168abae..5d6be4dd 100644 --- a/src/LanguageServerManager.ts +++ b/src/LanguageServerManager.ts @@ -535,7 +535,7 @@ export class LanguageServerManager { //collect `brightscript.bsdk` setting value from each workspaceFolder const folderResults = vscode.workspace.workspaceFolders?.reduce((acc, workspaceFolder) => { - const versionInfo = this.getWorkspaceBsdkInfo(vscode.workspace.workspaceFile); + const versionInfo = util.getConfiguration('brightscript', workspaceFolder).get('bsdk'); const parsed = this.parseVersionInfo(versionInfo, workspaceFolder.uri.fsPath); if (parsed) { acc.set(parsed.value, parsed); From 2042fd1f92a2b01fb3e4f1749cebe617b8e52625 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 18 Aug 2026 08:27:22 +0000 Subject: [PATCH 9/9] fix: pass workspaceFolder (not workspaceFile) in per-folder bsdk loop, add get() fallback MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In the folderResults reduce loop, getWorkspaceBsdkInfo was incorrectly called with vscode.workspace.workspaceFile (always the same global URI) instead of the current workspaceFolder, so all folders got the same — usually undefined — value and selectBrighterScriptVersion was never reached. Also extend getWorkspaceBsdkInfo to fall back to get() when inspect().workspaceValue is not set: workspaceValue carries the raw unresolved string from the .code-workspace file (needed for ${workspaceFolder} expansion), while per-folder settings have no workspaceValue and must be read via get(). --- src/LanguageServerManager.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/LanguageServerManager.ts b/src/LanguageServerManager.ts index 5d6be4dd..12adab97 100644 --- a/src/LanguageServerManager.ts +++ b/src/LanguageServerManager.ts @@ -535,7 +535,7 @@ export class LanguageServerManager { //collect `brightscript.bsdk` setting value from each workspaceFolder const folderResults = vscode.workspace.workspaceFolders?.reduce((acc, workspaceFolder) => { - const versionInfo = util.getConfiguration('brightscript', workspaceFolder).get('bsdk'); + const versionInfo = this.getWorkspaceBsdkInfo(workspaceFolder); const parsed = this.parseVersionInfo(versionInfo, workspaceFolder.uri.fsPath); if (parsed) { acc.set(parsed.value, parsed); @@ -565,7 +565,8 @@ export class LanguageServerManager { * value, so we expand ${workspaceFolder} and ${workspaceFolder:name} ourselves. */ private getWorkspaceBsdkInfo(workspaceFolder: vscode.ConfigurationScope) { - const rawValue = util.getConfiguration('brightscript', workspaceFolder).inspect('bsdk')?.workspaceValue?.trim?.(); + const config = util.getConfiguration('brightscript', workspaceFolder); + const rawValue = config.inspect('bsdk')?.workspaceValue?.trim?.() ?? config.get('bsdk')?.trim?.(); const hasVariable = rawValue?.startsWith('${'); if (!hasVariable) {