From cb7334c97f97a76b0110a03d3b3f2e6babcf4a0b Mon Sep 17 00:00:00 2001 From: Masahiro Hiramori Date: Wed, 11 Jun 2025 12:42:11 +0900 Subject: [PATCH] fix formatting cwd fallback and add test --- package-lock.json | 8 ++++++ package.json | 1 + src/ctags.ts | 7 +++++- src/linter/BaseLinter.ts | 4 ++- src/linter/IcarusLinter.ts | 5 ++-- src/linter/SlangLinter.ts | 5 +++- src/linter/VerilatorLinter.ts | 5 +++- src/providers/FormatPrivider.ts | 7 +++++- src/test/suite/format.test.ts | 44 +++++++++++++++++++++++++++++++++ 9 files changed, 79 insertions(+), 7 deletions(-) create mode 100644 src/test/suite/format.test.ts diff --git a/package-lock.json b/package-lock.json index aba02265..89ee5640 100644 --- a/package-lock.json +++ b/package-lock.json @@ -20,6 +20,7 @@ "@types/glob": "^8.1.0", "@types/mocha": "~10.0.6", "@types/node": "~22.10.0", + "@types/proxyquire": "^1.3.31", "@types/vscode": "^1.75.0", "@types/which": "^3.0.3", "@typescript-eslint/eslint-plugin": "^7.3.1", @@ -1065,6 +1066,13 @@ "undici-types": "~6.20.0" } }, + "node_modules/@types/proxyquire": { + "version": "1.3.31", + "resolved": "https://registry.npmjs.org/@types/proxyquire/-/proxyquire-1.3.31.tgz", + "integrity": "sha512-uALowNG2TSM1HNPMMOR0AJwv4aPYPhqB0xlEhkeRTMuto5hjoSPZkvgu1nbPUkz3gEPAHv4sy4DmKsurZiEfRQ==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/semver": { "version": "7.5.8", "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.8.tgz", diff --git a/package.json b/package.json index 8dd3d9b7..c9d8d597 100644 --- a/package.json +++ b/package.json @@ -607,6 +607,7 @@ "@types/glob": "^8.1.0", "@types/mocha": "~10.0.6", "@types/node": "~22.10.0", + "@types/proxyquire": "^1.3.31", "@types/vscode": "^1.75.0", "@types/which": "^3.0.3", "@typescript-eslint/eslint-plugin": "^7.3.1", diff --git a/src/ctags.ts b/src/ctags.ts index 695206e1..c1a5d544 100644 --- a/src/ctags.ts +++ b/src/ctags.ts @@ -2,6 +2,7 @@ import * as vscode from 'vscode'; import {exec as execNonPromise} from 'child_process'; import * as util from 'util'; +import * as path from 'path'; import { Logger } from './logger'; const exec = util.promisify(execNonPromise); @@ -366,7 +367,11 @@ export class CtagsManager { } // kick off async job for indexing for module.sv - let searchPattern = new vscode.RelativePattern(vscode.workspace.workspaceFolders[0], `**/${moduleToFind}.sv`); + const wsFolders = vscode.workspace.workspaceFolders; + const base = wsFolders && wsFolders.length > 0 + ? wsFolders[0] + : path.dirname(document.uri.fsPath); + let searchPattern = new vscode.RelativePattern(base, `**/${moduleToFind}.sv`); let files = await vscode.workspace.findFiles(searchPattern); if (files.length !== 0) { let file = await vscode.workspace.openTextDocument(files[0]); diff --git a/src/linter/BaseLinter.ts b/src/linter/BaseLinter.ts index d27ece25..df187501 100644 --- a/src/linter/BaseLinter.ts +++ b/src/linter/BaseLinter.ts @@ -19,7 +19,9 @@ export default abstract class BaseLinter { if (path.isAbsolute(inputPath)) { return inputPath; } - return path.join(vscode.workspace.workspaceFolders[0].uri.fsPath, inputPath); + const wsFolders = vscode.workspace.workspaceFolders; + const base = wsFolders && wsFolders.length > 0 ? wsFolders[0].uri.fsPath : ''; + return path.join(base, inputPath); } public startLint(doc: vscode.TextDocument) { diff --git a/src/linter/IcarusLinter.ts b/src/linter/IcarusLinter.ts index 0e6c678d..fdf5b333 100644 --- a/src/linter/IcarusLinter.ts +++ b/src/linter/IcarusLinter.ts @@ -72,10 +72,11 @@ export default class IcarusLinter extends BaseLinter { let command: string = binPath + ' ' + args.join(' '); // TODO: We have to apply the the #419 fix? + const wsFolders = vscode.workspace.workspaceFolders; let cwd: string = - this.runAtFileLocation || vscode.workspace.workspaceFolders === undefined + this.runAtFileLocation || !wsFolders || wsFolders.length === 0 ? path.dirname(doc.uri.fsPath) - : vscode.workspace.workspaceFolders[0].uri.fsPath; + : wsFolders[0].uri.fsPath; this.logger.info('Execute'); this.logger.info(' command: ', command); diff --git a/src/linter/SlangLinter.ts b/src/linter/SlangLinter.ts index aaa2d6e0..ebc7b499 100644 --- a/src/linter/SlangLinter.ts +++ b/src/linter/SlangLinter.ts @@ -78,11 +78,14 @@ export default class SlangLinter extends BaseLinter { args.push(`"${docUri}"`); let command: string = binPath + ' ' + args.join(' '); + const wsFolders = vscode.workspace.workspaceFolders; let cwd: string = this.runAtFileLocation ? isWindows ? cwdWin : docFolder - : vscode.workspace.workspaceFolders[0].uri.fsPath; + : wsFolders && wsFolders.length > 0 + ? wsFolders[0].uri.fsPath + : path.dirname(doc.uri.fsPath); this.logger.info('[slang] Execute'); this.logger.info('[slang] command: ' + command); diff --git a/src/linter/VerilatorLinter.ts b/src/linter/VerilatorLinter.ts index d393730d..fc547638 100644 --- a/src/linter/VerilatorLinter.ts +++ b/src/linter/VerilatorLinter.ts @@ -76,11 +76,14 @@ export default class VerilatorLinter extends BaseLinter { ? this.convertToWslPath(path.dirname(doc.uri.fsPath)) : path.dirname(doc.uri.fsPath).replace(/\\/g, '/') : path.dirname(doc.uri.fsPath); + const wsFolders = vscode.workspace.workspaceFolders; let cwd: string = this.runAtFileLocation ? isWindows ? path.dirname(doc.uri.fsPath.replace(/\\/g, '/')) : docFolder - : vscode.workspace.workspaceFolders[0].uri.fsPath; + : wsFolders && wsFolders.length > 0 + ? wsFolders[0].uri.fsPath + : path.dirname(doc.uri.fsPath); let verilator: string = isWindows ? this.useWSL ? 'wsl verilator' diff --git a/src/providers/FormatPrivider.ts b/src/providers/FormatPrivider.ts index 072137ce..b6b5c97e 100644 --- a/src/providers/FormatPrivider.ts +++ b/src/providers/FormatPrivider.ts @@ -68,7 +68,12 @@ abstract class FileBasedFormattingEditProvider implements vscode.DocumentFormatt let binPath: string = this.config.get('path'); this.logger.info('Executing command: ' + binPath + ' ' + args.join(' ')); try { - child_process.execFileSync(binPath, args, {cwd: vscode.workspace.workspaceFolders[0].uri.fsPath}); + const wsFolders = vscode.workspace.workspaceFolders; + const cwd = + wsFolders && wsFolders.length > 0 + ? wsFolders[0].uri.fsPath + : path.dirname(document.uri.fsPath); + child_process.execFileSync(binPath, args, { cwd }); let formattedText: string = tempFile.readFileSync({ encoding: 'utf-8' }); let wholeFileRange: vscode.Range = new vscode.Range( document.positionAt(0), diff --git a/src/test/suite/format.test.ts b/src/test/suite/format.test.ts new file mode 100644 index 00000000..4012b33a --- /dev/null +++ b/src/test/suite/format.test.ts @@ -0,0 +1,44 @@ +import * as assert from 'assert'; +import * as fs from 'fs'; +import * as os from 'os'; +import * as path from 'path'; +import * as proxyquire from 'proxyquire'; + +suite('Formatting Provider', () => { + test('does not crash without workspace', () => { + const tmp = path.join(os.tmpdir(), 'fmt.v'); + fs.writeFileSync(tmp, 'module m; endmodule'); + + const vscodeStub = { + workspace: { + getConfiguration: () => ({ get: () => '/bin/true' }), + workspaceFolders: undefined, + }, + Range: class { constructor(public start: any, public end: any) {} }, + Position: class { constructor(public line: number, public char: number) {} }, + TextEdit: { replace: (_r: any, _t: any) => ({}) }, + }; + + const providerModule = proxyquire('../../providers/FormatPrivider', { + vscode: vscodeStub, + }); + + const Provider = providerModule.VerilogFormatProvider; + const logger: any = { + info: () => {}, + error: () => {}, + warn: () => {}, + debug: () => {}, + }; + logger.getChild = () => logger; + const provider = new Provider(logger); + const doc = { + uri: { fsPath: tmp }, + getText: () => fs.readFileSync(tmp, 'utf8'), + positionAt: (_o: number) => new vscodeStub.Position(0, 0), + languageId: 'verilog', + }; + + assert.doesNotThrow(() => provider.provideDocumentFormattingEdits(doc as any, {} as any, {} as any)); + }); +});