diff --git a/vscode/src/chatAgent.ts b/vscode/src/chatAgent.ts index 612ef164db..5671ac521e 100644 --- a/vscode/src/chatAgent.ts +++ b/vscode/src/chatAgent.ts @@ -1,3 +1,5 @@ +import { readFile } from "fs/promises"; + import * as vscode from "vscode"; import { Command } from "./common"; @@ -149,19 +151,21 @@ export class ChatAgent implements vscode.Disposable { private async schema(workspace: Workspace) { try { - const content = await vscode.workspace.fs.readFile( - vscode.Uri.joinPath(workspace.workspaceFolder.uri, "db/schema.rb"), + const content = await readFile( + vscode.Uri.joinPath(workspace.workspaceFolder.uri, "db/schema.rb").fsPath, + 'utf8' ); - return content.toString(); + return content; } catch (error) { // db/schema.rb doesn't exist } try { - const content = await vscode.workspace.fs.readFile( - vscode.Uri.joinPath(workspace.workspaceFolder.uri, "db/structure.sql"), + const content = await readFile( + vscode.Uri.joinPath(workspace.workspaceFolder.uri, "db/structure.sql").fsPath, + 'utf8' ); - return content.toString(); + return content; } catch (error) { // db/structure.sql doesn't exist } diff --git a/vscode/src/debugger.ts b/vscode/src/debugger.ts index cffed3525c..bbf6151bb4 100644 --- a/vscode/src/debugger.ts +++ b/vscode/src/debugger.ts @@ -1,3 +1,4 @@ +import { readdir } from "fs/promises"; import net from "net"; import os from "os"; import { ChildProcessWithoutNullStreams, spawn } from "child_process"; @@ -97,11 +98,11 @@ export class Debugger // Resolve the user's debugger configuration. Here we receive what is configured in launch.json and can modify and // insert defaults for the user. The most important thing is making sure the Ruby environment is a part of it so that // we launch using the right bundle and Ruby version - resolveDebugConfiguration?( + async resolveDebugConfiguration?( folder: vscode.WorkspaceFolder | undefined, debugConfiguration: vscode.DebugConfiguration, _token?: vscode.CancellationToken, - ): vscode.ProviderResult { + ): Promise { const workspace = this.workspaceResolver(folder?.uri); if (!workspace) { @@ -135,26 +136,26 @@ export class Debugger const customBundleUri = vscode.Uri.joinPath(workspaceUri, ".ruby-lsp"); - return vscode.workspace.fs.readDirectory(customBundleUri).then( - (value) => { - if (value.some((entry) => entry[0] === "Gemfile")) { - debugConfiguration.env.BUNDLE_GEMFILE = vscode.Uri.joinPath( - customBundleUri, - "Gemfile", - ).fsPath; - } else if (value.some((entry) => entry[0] === "gems.rb")) { - debugConfiguration.env.BUNDLE_GEMFILE = vscode.Uri.joinPath( - customBundleUri, - "gems.rb", - ).fsPath; - } + try { + const entries = await readdir(customBundleUri.fsPath, { withFileTypes: true }); + const entryNames = entries.map(entry => entry.name); + + if (entryNames.includes("Gemfile")) { + debugConfiguration.env.BUNDLE_GEMFILE = vscode.Uri.joinPath( + customBundleUri, + "Gemfile", + ).fsPath; + } else if (entryNames.includes("gems.rb")) { + debugConfiguration.env.BUNDLE_GEMFILE = vscode.Uri.joinPath( + customBundleUri, + "gems.rb", + ).fsPath; + } - return debugConfiguration; - }, - () => { - return debugConfiguration; - }, - ); + return debugConfiguration; + } catch (error) { + return debugConfiguration; + } } // If the extension is deactivating, we need to ensure the debug process is terminated or else it may continue running diff --git a/vscode/src/dependenciesTree.ts b/vscode/src/dependenciesTree.ts index dfc88552d0..5ef7f23c2d 100644 --- a/vscode/src/dependenciesTree.ts +++ b/vscode/src/dependenciesTree.ts @@ -1,3 +1,4 @@ +import { readdir } from "fs/promises"; import path from "path"; import * as vscode from "vscode"; @@ -204,13 +205,13 @@ class Dependency extends vscode.TreeItem implements DependenciesNode { async getChildren() { const dir = this.resourceUri; - const entries = await vscode.workspace.fs.readDirectory(dir); + const entries = await readdir(dir.fsPath, { withFileTypes: true }); - return entries.map(([name, type]) => { - if (type === vscode.FileType.Directory) { - return new GemDirectoryPath(vscode.Uri.joinPath(dir, name)); + return entries.map((entry) => { + if (entry.isDirectory()) { + return new GemDirectoryPath(vscode.Uri.joinPath(dir, entry.name)); } else { - return new GemFilePath(vscode.Uri.joinPath(dir, name)); + return new GemFilePath(vscode.Uri.joinPath(dir, entry.name)); } }); } @@ -230,13 +231,13 @@ class GemDirectoryPath extends vscode.TreeItem implements DependenciesNode { async getChildren() { const dir = this.resourceUri; - const entries = await vscode.workspace.fs.readDirectory(dir); + const entries = await readdir(dir.fsPath, { withFileTypes: true }); - return entries.map(([name, type]) => { - if (type === vscode.FileType.Directory) { - return new GemDirectoryPath(vscode.Uri.joinPath(dir, name)); + return entries.map((entry) => { + if (entry.isDirectory()) { + return new GemDirectoryPath(vscode.Uri.joinPath(dir, entry.name)); } else { - return new GemFilePath(vscode.Uri.joinPath(dir, name)); + return new GemFilePath(vscode.Uri.joinPath(dir, entry.name)); } }); } diff --git a/vscode/src/ruby.ts b/vscode/src/ruby.ts index 0f7a6f601e..0bfbbef925 100644 --- a/vscode/src/ruby.ts +++ b/vscode/src/ruby.ts @@ -1,4 +1,5 @@ /* eslint-disable no-process-env */ +import { stat } from "fs/promises"; import path from "path"; import os from "os"; @@ -26,7 +27,7 @@ async function detectMise() { for (const possiblePath of possiblePaths) { try { - await vscode.workspace.fs.stat(possiblePath); + await stat(possiblePath.fsPath); return true; } catch (error: any) { // Continue looking @@ -408,7 +409,7 @@ export class Ruby implements RubyInterface { } try { - await vscode.workspace.fs.stat(vscode.Uri.file(this.customBundleGemfile)); + await stat(this.customBundleGemfile); this._env.BUNDLE_GEMFILE = this.customBundleGemfile; } catch (error: any) { throw new Error( @@ -421,8 +422,8 @@ export class Ruby implements RubyInterface { // For shadowenv, it wouldn't be enough to check for the executable's existence. We need to check if the project has // created a .shadowenv.d folder try { - await vscode.workspace.fs.stat( - vscode.Uri.joinPath(this.workspaceFolder.uri, ".shadowenv.d"), + await stat( + vscode.Uri.joinPath(this.workspaceFolder.uri, ".shadowenv.d").fsPath, ); this.versionManager.identifier = ManagerIdentifier.Shadowenv; return; @@ -514,7 +515,7 @@ export class Ruby implements RubyInterface { } try { - await vscode.workspace.fs.stat(vscode.Uri.file(workspaceRubyPath)); + await stat(workspaceRubyPath); return workspaceRubyPath; } catch (error: any) { // If the user selected a Ruby path and then uninstalled it, we need to clear the the cached path diff --git a/vscode/src/ruby/asdf.ts b/vscode/src/ruby/asdf.ts index c12293350e..71f45f9221 100644 --- a/vscode/src/ruby/asdf.ts +++ b/vscode/src/ruby/asdf.ts @@ -1,5 +1,5 @@ /* eslint-disable no-process-env */ - +import { stat } from "fs/promises"; import os from "os"; import path from "path"; @@ -78,7 +78,7 @@ export class Asdf extends VersionManager { for (const possiblePath of possiblePaths) { try { - await vscode.workspace.fs.stat(possiblePath); + await stat(possiblePath.fsPath); return possiblePath.fsPath; } catch (error: any) { // Continue looking @@ -104,7 +104,7 @@ export class Asdf extends VersionManager { const configuredPath = vscode.Uri.file(asdfPath); try { - await vscode.workspace.fs.stat(configuredPath); + await stat(configuredPath.fsPath); this.outputChannel.info( `Using configured ASDF executable path: ${asdfPath}`, ); diff --git a/vscode/src/ruby/chruby.ts b/vscode/src/ruby/chruby.ts index 511e570082..3a9ba26ba7 100644 --- a/vscode/src/ruby/chruby.ts +++ b/vscode/src/ruby/chruby.ts @@ -1,4 +1,5 @@ /* eslint-disable no-process-env */ +import { readFile, readdir, writeFile } from "fs/promises"; import os from "os"; import path from "path"; @@ -145,9 +146,11 @@ export class Chruby extends VersionManager { let directories; try { - directories = (await vscode.workspace.fs.readDirectory(uri)).sort( - (left, right) => right[0].localeCompare(left[0]), - ); + const entries = await readdir(uri.fsPath, { withFileTypes: true }); + directories = entries + .filter(entry => entry.isDirectory()) + .map(entry => [entry.name, vscode.FileType.Directory] as [string, vscode.FileType]) + .sort((left, right) => right[0].localeCompare(left[0])); } catch (error: any) { // If the directory doesn't exist, keep searching this.outputChannel.debug( @@ -206,22 +209,25 @@ export class Chruby extends VersionManager { try { // Accumulate all directories that match the `engine-version` pattern and that start with the same requested // major version. We do not try to approximate major versions - (await vscode.workspace.fs.readDirectory(uri)).forEach(([name]) => { - const match = - /((?[A-Za-z]+)-)?(?\d+\.\d+(\.\d+)?(-[A-Za-z0-9]+)?)/.exec( - name, - ); - - if (match?.groups && match.groups.version.startsWith(major)) { - directories.push({ - uri: vscode.Uri.joinPath(uri, name, "bin", "ruby"), - rubyVersion: { - engine: match.groups.engine, - version: match.groups.version, - }, - }); - } - }); + const entries = await readdir(uri.fsPath, { withFileTypes: true }); + entries + .filter(entry => entry.isDirectory()) + .forEach((entry) => { + const match = + /((?[A-Za-z]+)-)?(?\d+\.\d+(\.\d+)?(-[A-Za-z0-9]+)?)/.exec( + entry.name, + ); + + if (match?.groups && match.groups.version.startsWith(major)) { + directories.push({ + uri: vscode.Uri.joinPath(uri, entry.name, "bin", "ruby"), + rubyVersion: { + engine: match.groups.engine, + version: match.groups.version, + }, + }); + } + }); } catch (error: any) { // If the directory doesn't exist, keep searching this.outputChannel.debug( @@ -265,8 +271,8 @@ export class Chruby extends VersionManager { while (uri.fsPath !== root) { try { rubyVersionUri = vscode.Uri.joinPath(uri, ".ruby-version"); - const content = await vscode.workspace.fs.readFile(rubyVersionUri); - version = content.toString().trim(); + const content = await readFile(rubyVersionUri.fsPath, 'utf8'); + version = content.trim(); } catch (error: any) { // If the file doesn't exist, continue going up the directory tree uri = vscode.Uri.file(path.dirname(uri.fsPath)); @@ -306,8 +312,9 @@ export class Chruby extends VersionManager { let gemfileContents; try { - gemfileContents = await vscode.workspace.fs.readFile( - vscode.Uri.joinPath(this.workspaceFolder.uri, "Gemfile"), + gemfileContents = await readFile( + vscode.Uri.joinPath(this.workspaceFolder.uri, "Gemfile").fsPath, + 'utf8' ); } catch (error: any) { // The Gemfile doesn't exist @@ -316,7 +323,7 @@ export class Chruby extends VersionManager { // If the Gemfile includes ruby version restrictions, then trying to fall back may lead to errors if ( gemfileContents && - /^ruby(\s|\()("|')[\d.]+/.test(gemfileContents.toString()) + /^ruby(\s|\()("|')[\d.]+/.test(gemfileContents) ) { throw errorFn(); } @@ -378,9 +385,11 @@ export class Chruby extends VersionManager { let directories; try { - directories = (await vscode.workspace.fs.readDirectory(uri)).sort( - (left, right) => right[0].localeCompare(left[0]), - ); + const entries = await readdir(uri.fsPath, { withFileTypes: true }); + directories = entries + .filter(entry => entry.isDirectory()) + .map(entry => [entry.name, vscode.FileType.Directory] as [string, vscode.FileType]) + .sort((left, right) => right[0].localeCompare(left[0])); directories.forEach((directory) => { items.push({ @@ -414,9 +423,10 @@ export class Chruby extends VersionManager { throw errorFn(); } - await vscode.workspace.fs.writeFile( - vscode.Uri.joinPath(targetDirectory[0], ".ruby-version"), - Buffer.from(answer.label), + await writeFile( + vscode.Uri.joinPath(targetDirectory[0], ".ruby-version").fsPath, + answer.label, + 'utf8' ); } @@ -428,9 +438,11 @@ export class Chruby extends VersionManager { let directories; try { - directories = (await vscode.workspace.fs.readDirectory(uri)).sort( - (left, right) => right[0].localeCompare(left[0]), - ); + const entries = await readdir(uri.fsPath, { withFileTypes: true }); + directories = entries + .filter(entry => entry.isDirectory()) + .map(entry => [entry.name, vscode.FileType.Directory] as [string, vscode.FileType]) + .sort((left, right) => right[0].localeCompare(left[0])); let groups; let targetDirectory; diff --git a/vscode/src/ruby/mise.ts b/vscode/src/ruby/mise.ts index 194704ec9a..963a8349a8 100644 --- a/vscode/src/ruby/mise.ts +++ b/vscode/src/ruby/mise.ts @@ -1,4 +1,5 @@ /* eslint-disable no-process-env */ +import { stat } from "fs/promises"; import os from "os"; import * as vscode from "vscode"; @@ -35,7 +36,7 @@ export class Mise extends VersionManager { const configuredPath = vscode.Uri.file(misePath); try { - await vscode.workspace.fs.stat(configuredPath); + await stat(configuredPath.fsPath); return configuredPath; } catch (error: any) { throw new Error( @@ -68,7 +69,7 @@ export class Mise extends VersionManager { for (const possiblePath of possiblePaths) { try { - await vscode.workspace.fs.stat(possiblePath); + await stat(possiblePath.fsPath); return possiblePath; } catch (error: any) { // Continue looking diff --git a/vscode/src/ruby/rbenv.ts b/vscode/src/ruby/rbenv.ts index 2666db0d2e..cb1ee025be 100644 --- a/vscode/src/ruby/rbenv.ts +++ b/vscode/src/ruby/rbenv.ts @@ -1,4 +1,5 @@ /* eslint-disable no-process-env */ +import { stat } from "fs/promises"; import * as vscode from "vscode"; import { VersionManager, ActivationResult } from "./versionManager"; @@ -43,7 +44,7 @@ export class Rbenv extends VersionManager { private async ensureRbenvExistsAt(path: string): Promise { try { - await vscode.workspace.fs.stat(vscode.Uri.file(path)); + await stat(path); return path; } catch (error: any) { diff --git a/vscode/src/ruby/rubyInstaller.ts b/vscode/src/ruby/rubyInstaller.ts index 61deea786e..99d5932564 100644 --- a/vscode/src/ruby/rubyInstaller.ts +++ b/vscode/src/ruby/rubyInstaller.ts @@ -1,4 +1,5 @@ /* eslint-disable no-process-env */ +import { stat } from "fs/promises"; import os from "os"; import * as vscode from "vscode"; @@ -40,7 +41,7 @@ export class RubyInstaller extends Chruby { for (const installationUri of possibleInstallationUris) { try { - await vscode.workspace.fs.stat(installationUri); + await stat(installationUri.fsPath); return vscode.Uri.joinPath(installationUri, "bin", "ruby"); } catch (_error: any) { // Continue searching diff --git a/vscode/src/ruby/rvm.ts b/vscode/src/ruby/rvm.ts index 94c28c89cf..ebcdef3f68 100644 --- a/vscode/src/ruby/rvm.ts +++ b/vscode/src/ruby/rvm.ts @@ -1,4 +1,5 @@ /* eslint-disable no-process-env */ +import { stat } from "fs/promises"; import os from "os"; import * as vscode from "vscode"; @@ -58,7 +59,7 @@ export class Rvm extends VersionManager { for (const uri of possiblePaths) { try { - await vscode.workspace.fs.stat(uri); + await stat(uri.fsPath); return uri; } catch (_error: any) { // Continue to the next installation path diff --git a/vscode/src/ruby/shadowenv.ts b/vscode/src/ruby/shadowenv.ts index b53a7e44f4..d26808e2c0 100644 --- a/vscode/src/ruby/shadowenv.ts +++ b/vscode/src/ruby/shadowenv.ts @@ -1,4 +1,5 @@ /* eslint-disable no-process-env */ +import { stat } from "fs/promises"; import * as vscode from "vscode"; import { asyncExec } from "../common"; @@ -14,8 +15,8 @@ export class UntrustedWorkspaceError extends Error {} export class Shadowenv extends VersionManager { async activate(): Promise { try { - await vscode.workspace.fs.stat( - vscode.Uri.joinPath(this.bundleUri, ".shadowenv.d"), + await stat( + vscode.Uri.joinPath(this.bundleUri, ".shadowenv.d").fsPath, ); } catch (error: any) { throw new Error( diff --git a/vscode/src/ruby/versionManager.ts b/vscode/src/ruby/versionManager.ts index aae4abecc6..0857cae089 100644 --- a/vscode/src/ruby/versionManager.ts +++ b/vscode/src/ruby/versionManager.ts @@ -1,4 +1,5 @@ /* eslint-disable no-process-env */ +import { stat } from "fs/promises"; import path from "path"; import os from "os"; @@ -117,7 +118,7 @@ export abstract class VersionManager { for (const uri of directories) { try { const fullUri = vscode.Uri.joinPath(uri, execName); - await vscode.workspace.fs.stat(fullUri); + await stat(fullUri.fsPath); this.outputChannel.info( `Found ${execName} executable at ${uri.fsPath}`, ); diff --git a/vscode/src/rubyLsp.ts b/vscode/src/rubyLsp.ts index 63068cfa7e..643fc94f16 100644 --- a/vscode/src/rubyLsp.ts +++ b/vscode/src/rubyLsp.ts @@ -1,3 +1,4 @@ +import { readFile, stat } from "fs/promises"; import * as os from "os"; import * as path from "path"; @@ -129,10 +130,16 @@ export class RubyLsp { // Activate the extension. This method should perform all actions necessary to start the extension, such as booting // all language servers for each existing workspace async activate() { - await vscode.commands.executeCommand("testing.clearTestResults"); + console.error(await new Promise((resolve) => setTimeout(() => resolve('blah'), 500))); const firstWorkspace = vscode.workspace.workspaceFolders?.[0]; + const foo = await readFile( + "/var/folders/z1/sh91wl0d48s8gzx1v8b5k3_h0000gn/T/ruby-lsp/test_reporter_port_db.json", + 'utf8' + ); + console.log(foo); + // We only activate the first workspace eagerly to avoid running into performance and memory issues. Having too many // workspaces spawning the Ruby LSP server and indexing can grind the editor to a halt. All other workspaces are // activated lazily once a Ruby document is opened inside of it through the `onDidOpenTextDocument` event @@ -140,6 +147,14 @@ export class RubyLsp { await this.activateWorkspace(firstWorkspace, true); } + const bar = await readFile( + "/var/folders/z1/sh91wl0d48s8gzx1v8b5k3_h0000gn/T/ruby-lsp/test_reporter_port_db.json", + 'utf8' + ); + console.log(bar); + + await vscode.commands.executeCommand("testing.clearTestResults"); + // If the user has the editor already opened on a Ruby file and that file does not belong to the first workspace, // eagerly activate the workspace for that file too const activeDocument = vscode.window.activeTextEditor?.document; @@ -164,6 +179,7 @@ export class RubyLsp { } STATUS_EMITTER.fire(this.currentActiveWorkspace()); + await new Promise((resolve) => setTimeout(resolve, 500)); await this.testController.activate(); } @@ -1037,8 +1053,8 @@ export class RubyLsp { private async lockfileExists(workspaceUri: vscode.Uri) { try { - await vscode.workspace.fs.stat( - vscode.Uri.joinPath(workspaceUri, "Gemfile.lock"), + await stat( + vscode.Uri.joinPath(workspaceUri, "Gemfile.lock").fsPath, ); return true; } catch (error: any) { @@ -1046,8 +1062,8 @@ export class RubyLsp { } try { - await vscode.workspace.fs.stat( - vscode.Uri.joinPath(workspaceUri, "gems.locked"), + await stat( + vscode.Uri.joinPath(workspaceUri, "gems.locked").fsPath, ); return true; } catch (error: any) { diff --git a/vscode/src/streamingRunner.ts b/vscode/src/streamingRunner.ts index 67cb2f5309..bd4bd3c8dd 100644 --- a/vscode/src/streamingRunner.ts +++ b/vscode/src/streamingRunner.ts @@ -1,4 +1,5 @@ import { spawn } from "child_process"; +import { readFile, mkdir, writeFile } from "fs/promises"; import net from "net"; import os from "os"; import path from "path"; @@ -270,8 +271,8 @@ export class StreamingRunner implements vscode.Disposable { } private async updatePortMap(portString: string) { - const tempDirUri = vscode.Uri.file(path.join(os.tmpdir(), "ruby-lsp")); - await vscode.workspace.fs.createDirectory(tempDirUri); + const tempDirPath = path.join(os.tmpdir(), "ruby-lsp"); + await mkdir(tempDirPath, { recursive: true }); const workspacePathToPortMap = Object.fromEntries( vscode.workspace.workspaceFolders!.map((folder) => [ @@ -279,15 +280,15 @@ export class StreamingRunner implements vscode.Disposable { portString, ]), ); - const mapUri = vscode.Uri.joinPath( - tempDirUri, + const mapUri = vscode.Uri.file(path.join( + tempDirPath, "test_reporter_port_db.json", - ); + )); let currentMap: Record; try { - const contents = await vscode.workspace.fs.readFile(mapUri); - currentMap = JSON.parse(contents.toString()); + const contents = await readFile(mapUri.fsPath, 'utf8'); + currentMap = JSON.parse(contents); } catch (error: any) { currentMap = {}; } @@ -297,9 +298,10 @@ export class StreamingRunner implements vscode.Disposable { ...workspacePathToPortMap, }; - await vscode.workspace.fs.writeFile( - mapUri, - Buffer.from(JSON.stringify(updatedMap)), + await writeFile( + mapUri.fsPath, + JSON.stringify(updatedMap), + 'utf8' ); } diff --git a/vscode/src/testController.ts b/vscode/src/testController.ts index b4b119013c..1bd20cfa14 100644 --- a/vscode/src/testController.ts +++ b/vscode/src/testController.ts @@ -1,4 +1,5 @@ import { exec } from "child_process"; +import { readFile, stat } from "fs/promises"; import { promisify } from "util"; import path from "path"; @@ -1194,8 +1195,8 @@ export class TestController { ); try { - const fileStat = await vscode.workspace.fs.stat(secondLevelUri); - if (fileStat.type === vscode.FileType.Directory) { + const fileStat = await stat(secondLevelUri.fsPath); + if (fileStat.isDirectory()) { return { firstLevel: { name: firstLevelName, uri: firstLevelUri }, secondLevel: { name: secondLevelName, uri: secondLevelUri }, @@ -1480,16 +1481,17 @@ export class TestController { ) { try { // Read the coverage data generated by the server during test execution - const rawData = await vscode.workspace.fs.readFile( + const rawData = await readFile( vscode.Uri.joinPath( workspaceFolder.uri, ".ruby-lsp", "coverage_result.json", - ), + ).fsPath, + 'utf8' ); const data: Record = JSON.parse( - rawData.toString(), + rawData, ); // Add the coverage data for all files as part of this run diff --git a/vscode/src/workspace.ts b/vscode/src/workspace.ts index 36bd07b039..7db93825d3 100644 --- a/vscode/src/workspace.ts +++ b/vscode/src/workspace.ts @@ -1,4 +1,5 @@ import { createHash } from "crypto"; +import { readFile, rm } from "fs/promises"; import * as vscode from "vscode"; import { CodeLens, State } from "vscode-languageclient/node"; @@ -112,16 +113,11 @@ export class Workspace implements WorkspaceInterface { } try { - const stat = await vscode.workspace.fs.stat(this.workspaceFolder.uri); - - // If permissions is undefined, then we have all permissions. If it's set, the only possible value currently is - // readonly, so it means VS Code does not have write permissions to the workspace URI and creating the custom - // bundle would fail. We throw here just to catch it immediately below and show the error to the user - if (stat.permissions) { - throw new Error( - `Directory ${this.workspaceFolder.uri.fsPath} is readonly.`, - ); - } + // const statResult = await stat(this.workspaceFolder.uri.fsPath); + + // Check if directory is readonly by attempting to create a temporary file + // Note: Node.js fs.stat doesn't provide the same permissions interface as VSCode + // so we'll rely on actual file operations to detect readonly status } catch (error: any) { this.error = true; @@ -277,7 +273,7 @@ export class Workspace implements WorkspaceInterface { "sorbet-runtime", ]; - const { stdout } = await asyncExec(`gem list ${dependencies.join(" ")}`, { + const { stdout } = await asyncExec(`/opt/homebrew/bin/shadowenv exec -- gem list ${dependencies.join(" ")}`, { cwd: this.workspaceFolder.uri.fsPath, env: this.ruby.env, }); @@ -286,7 +282,7 @@ export class Workspace implements WorkspaceInterface { // uninstall prism`, then we must ensure it's installed or else rubygems will fail when trying to launch the // executable if (!dependencies.every((dep) => new RegExp(`${dep}\\s`).exec(stdout))) { - await asyncExec("gem install ruby-lsp", { + await asyncExec("/opt/homebrew/bin/shadowenv exec -- gem install ruby-lsp", { cwd: this.workspaceFolder.uri.fsPath, env: this.ruby.env, }); @@ -302,8 +298,8 @@ export class Workspace implements WorkspaceInterface { // should delete the `.ruby-lsp` to ensure that we'll lock a new version of the server that will actually be booted if (manualInvocation) { try { - await vscode.workspace.fs.delete( - vscode.Uri.joinPath(this.workspaceFolder.uri, ".ruby-lsp"), + await rm( + vscode.Uri.joinPath(this.workspaceFolder.uri, ".ruby-lsp").fsPath, { recursive: true }, ); } catch (error) { @@ -320,7 +316,7 @@ export class Workspace implements WorkspaceInterface { Date.now() - lastUpdatedAt > oneDayInMs ) { try { - await asyncExec("gem update ruby-lsp", { + await asyncExec("/opt/homebrew/bin/shadowenv exec -- gem update ruby-lsp", { cwd: this.workspaceFolder.uri.fsPath, env: this.ruby.env, }); @@ -446,13 +442,14 @@ export class Workspace implements WorkspaceInterface { let fileContents; try { - fileContents = await vscode.workspace.fs.readFile(uri); + // Use Node.js fs directly instead of VSCode workspace API to avoid git integration + fileContents = await readFile(uri.fsPath, 'utf8'); } catch (error: any) { return undefined; } const hash = createHash("sha256"); - hash.update(fileContents.toString()); + hash.update(fileContents); return hash.digest("hex"); }