From 2456512d6a21eb4b5a04f49179bf46390494fc36 Mon Sep 17 00:00:00 2001 From: Bela VanderVoort Date: Sun, 18 Aug 2024 13:27:49 -0500 Subject: [PATCH] =?UTF-8?q?Modify=20VSCode=20extension=20to=20use=20dotnet?= =?UTF-8?q?=20tool=20list=20+=20only=20warn=20to=20instal=E2=80=A6=20(#132?= =?UTF-8?q?3)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This modifies the VSCode extension in the following ways - Instead of looking for dotnet-tools.json files in the current directory and parent directories the extension runs `dotnet tool list` and parses the outpud looking for the csharpier version. - Instead of running `dotnet csharpier --version` to look for the global version it repeats the above step with `dotnet tool list -g` - The "You need to install csharpier" window was popping up every time you opened a file if it couldn't find an install. The other extensions were limited to showing the warning a single time. VSCode now only shows it a single time. - Files under `/DecompilationMetadataAsSourceFileProvider/` will never have the warning show. This is where the c# dev kit decompiles code. #1267 --- Src/CSharpier.VSCode/CHANGELOG.md | 5 ++ Src/CSharpier.VSCode/package.json | 2 +- .../src/CSharpierProcessProvider.ts | 90 ++++++++++--------- Src/CSharpier.VSCode/src/InstallerService.ts | 14 +++ .../src/RunFunctionsUntilResultFound.ts | 10 +++ 5 files changed, 80 insertions(+), 41 deletions(-) create mode 100644 Src/CSharpier.VSCode/src/RunFunctionsUntilResultFound.ts diff --git a/Src/CSharpier.VSCode/CHANGELOG.md b/Src/CSharpier.VSCode/CHANGELOG.md index e90d327b7..5b41c6e9d 100644 --- a/Src/CSharpier.VSCode/CHANGELOG.md +++ b/Src/CSharpier.VSCode/CHANGELOG.md @@ -1,3 +1,8 @@ +## 1.8.0 +- Use `dotnet tool list` to look for both local and global installs of csharpier +- Only show the "you need to install csharpier" notification once per run +- Exclude some directories from the "you need to install csharpier" notification + ## [1.7.3] - Only use CSharpier Server on 0.29.0+ - Add option to bypass csharpier server. diff --git a/Src/CSharpier.VSCode/package.json b/Src/CSharpier.VSCode/package.json index e7c7efc25..0abea984e 100644 --- a/Src/CSharpier.VSCode/package.json +++ b/Src/CSharpier.VSCode/package.json @@ -2,7 +2,7 @@ "name": "csharpier-vscode", "displayName": "CSharpier - Code formatter", "description": "Code formatter using csharpier", - "version": "1.7.3", + "version": "1.8.0", "publisher": "csharpier", "author": "CSharpier", "homepage": "https://marketplace.visualstudio.com/items?itemName=csharpier.csharpier-vscode", diff --git a/Src/CSharpier.VSCode/src/CSharpierProcessProvider.ts b/Src/CSharpier.VSCode/src/CSharpierProcessProvider.ts index 953c34b75..b19ad2516 100644 --- a/Src/CSharpier.VSCode/src/CSharpierProcessProvider.ts +++ b/Src/CSharpier.VSCode/src/CSharpierProcessProvider.ts @@ -13,6 +13,7 @@ import { execDotNet } from "./DotNetProvider"; import { NullCSharpierProcess } from "./NullCSharpierProcess"; import { CSharpierProcessServer } from "./CSharpierProcessServer"; import { ICSharpierProcess2 } from "./ICSharpierProcess"; +import { runFunctionsUntilResultFound } from "./RunFunctionsUntilResultFound"; export class CSharpierProcessProvider implements Disposable { warnedForOldVersion = false; @@ -37,7 +38,8 @@ export class CSharpierProcessProvider implements Disposable { ); this.disableCSharpierServer = - workspace.getConfiguration("csharpier").get("dev.disableCSharpierServer") ?? false; + workspace.getConfiguration("csharpier").get("dev.disableCSharpierServer") ?? + false; window.onDidChangeActiveTextEditor((event: TextEditor | undefined) => { if (event?.document?.languageId !== "csharp") { @@ -114,6 +116,51 @@ export class CSharpierProcessProvider implements Disposable { }; private getCSharpierVersion = (directoryThatContainsFile: string): string => { + const csharpierVersion = runFunctionsUntilResultFound( + () => this.findVersionInCsProjOfParentsDirectories(directoryThatContainsFile), + () => this.findCSharpierVersionInToolOutput(directoryThatContainsFile, false), + () => this.findCSharpierVersionInToolOutput(directoryThatContainsFile, true), + ); + + if (!csharpierVersion) { + return ""; + } + + const versionWithoutHash = csharpierVersion.split("+")[0]; + this.logger.debug(`Using ${versionWithoutHash} as the version number.`); + return versionWithoutHash; + }; + + private findCSharpierVersionInToolOutput = ( + directoryThatContainsFile: string, + isGlobal: boolean, + ) => { + const command = `tool list${isGlobal ? " -g" : ""}`; + const output = execDotNet(command, directoryThatContainsFile).toString().trim(); + + this.logger.debug(`Running 'dotnet ${command}' to look for version`); + this.logger.debug(`Output was: \n${output}`); + + const lines = output + .split("\n") + .map(line => line.trim()) + .filter(line => line.length > 0); + + // The first two lines are headers + for (let i = 2; i < lines.length; i++) { + const columns = lines[i].split(/\s{2,}/); + if (columns.length >= 2) { + if (columns[0].toLowerCase() === "csharpier") { + return columns[1]; + } + } + } + }; + + private findVersionInCsProjOfParentsDirectories = (directoryThatContainsFile: string) => { + this.logger.debug( + `Looking for csproj in or above ${directoryThatContainsFile} that references CSharpier.MsBuild`, + ); let currentDirectory = directoryThatContainsFile; let parentNumber = 0; while (parentNumber < 30) { @@ -122,17 +169,6 @@ export class CSharpierProcessProvider implements Disposable { return csProjVersion; } - const dotnetToolsPath = path.join(currentDirectory, ".config/dotnet-tools.json"); - this.logger.debug(`Looking for ${dotnetToolsPath}`); - if (fs.existsSync(dotnetToolsPath)) { - const data = JSON.parse(fs.readFileSync(dotnetToolsPath).toString()); - const version = data.tools.csharpier?.version; - if (version) { - this.logger.debug("Found version " + version + " in " + dotnetToolsPath); - return version; - } - } - const nextDirectory = path.join(currentDirectory, ".."); if (nextDirectory === currentDirectory) { break; @@ -140,28 +176,6 @@ export class CSharpierProcessProvider implements Disposable { currentDirectory = nextDirectory; parentNumber++; } - - this.logger.debug( - "Unable to find dotnet-tools.json, falling back to running dotnet csharpier --version", - ); - - let outputFromCsharpier: string; - - try { - outputFromCsharpier = execDotNet(`csharpier --version`, directoryThatContainsFile) - .toString() - .trim(); - - this.logger.debug(`dotnet csharpier --version output: ${outputFromCsharpier}`); - const versionWithoutHash = outputFromCsharpier.split("+")[0]; - this.logger.debug(`Using ${versionWithoutHash} as the version number.`); - return versionWithoutHash; - } catch (error: any) { - const message = !error.stderr ? error.toString() : error.stderr.toString(); - - this.logger.debug("dotnet csharpier --version failed with " + message); - return ""; - } }; private findVersionInCsProj = (currentDirectory: string) => { @@ -240,13 +254,9 @@ export class CSharpierProcessProvider implements Disposable { version, ); } else if (semver.gte(version, "0.12.0")) { - if ( - semver.gte(version, serverVersion) - && this.disableCSharpierServer - ) - { + if (semver.gte(version, serverVersion) && this.disableCSharpierServer) { this.logger.debug( - "CSharpier server is disabled, falling back to piping via stdin" + "CSharpier server is disabled, falling back to piping via stdin", ); } diff --git a/Src/CSharpier.VSCode/src/InstallerService.ts b/Src/CSharpier.VSCode/src/InstallerService.ts index d89f25962..e82a27383 100644 --- a/Src/CSharpier.VSCode/src/InstallerService.ts +++ b/Src/CSharpier.VSCode/src/InstallerService.ts @@ -13,6 +13,7 @@ export class InstallerService { logger: Logger; killRunningProcesses: () => void; extension: Extension; + warnedAlready = false; constructor(logger: Logger, killRunningProcesses: () => void, extension: Extension) { this.logger = logger; @@ -21,6 +22,11 @@ export class InstallerService { } public displayInstallNeededMessage = (directoryThatContainsFile: string) => { + if (this.warnedAlready || this.ignoreDirectory(directoryThatContainsFile)) { + return; + } + + this.warnedAlready = true; this.logger.error("CSharpier was not found so files may not be formatted."); this.logger.info(this.extension.extensionKind); @@ -119,4 +125,12 @@ export class InstallerService { }, ); }; + + private ignoreDirectory(directoryThatContainsFile: string) { + const normalizedPath = directoryThatContainsFile.replace(/\\/g, "/"); + return ( + normalizedPath.indexOf("/DecompilationMetadataAsSourceFileProvider/") >= 0 || + normalizedPath === "/" + ); + } } diff --git a/Src/CSharpier.VSCode/src/RunFunctionsUntilResultFound.ts b/Src/CSharpier.VSCode/src/RunFunctionsUntilResultFound.ts new file mode 100644 index 000000000..81aca0962 --- /dev/null +++ b/Src/CSharpier.VSCode/src/RunFunctionsUntilResultFound.ts @@ -0,0 +1,10 @@ +export const runFunctionsUntilResultFound = (...functions: (() => T)[]) => { + for (const possibleFunction of functions) { + const result = possibleFunction(); + if (result) { + return result; + } + } + + return undefined; +};