Skip to content

Commit

Permalink
Merge pull request #19 from JoseVSeb/alpha
Browse files Browse the repository at this point in the history
Alpha
  • Loading branch information
JoseVSeb authored Feb 18, 2024
2 parents bed73bc + 230405e commit f783888
Show file tree
Hide file tree
Showing 23 changed files with 530 additions and 416 deletions.
5 changes: 4 additions & 1 deletion .eslintrc → .eslintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@
"rules": {
"@typescript-eslint/naming-convention": "warn",
"@typescript-eslint/semi": "warn",
"@typescript-eslint/no-unused-vars": [
"error",
{ "ignoreRestSiblings": true }
],
"curly": "warn",
"eqeqeq": "warn",
"no-throw-literal": "warn",
Expand All @@ -24,4 +28,3 @@
},
"ignorePatterns": ["out", "dist", "**/*.d.ts"]
}

10 changes: 9 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,19 @@ Format your java files using Google Java Format program which follows Google Jav
This extension contributes the following settings:

* `java.format.settings.google.executable`: *Not Recommended.* Specifies url or file path to [Google Java Format jar executable](https://github.com/google/google-java-format/releases). Overrides `java.format.settings.google.version`.
* `java.format.settings.google.version`: *Recommended.* Specifies version to be used of [Google Java Format jar executable](https://github.com/google/google-java-format/releases) in format `{major}.{minor}.{patch}`. Default: `latest`.
* `java.format.settings.google.version`: *Recommended.* Specifies version to be used of [Google Java Format executable](https://github.com/google/google-java-format/releases) in format `{major}.{minor}.{patch}`. Default: `latest`.
* `java.format.settings.google.mode`: Specifies the runtime mode of [Google Java Format](https://github.com/google/google-java-format/releases). Used with `java.format.settings.google.version`. Default: `native-binary`.
* `java.format.settings.google.extra`: Extra CLI arguments to pass to [Google Java Format](https://github.com/google/google-java-format).

Please refer [Google Java Format repository](https://github.com/google/google-java-format) for available versions and CLI arguments.

## Extension Commands

This extension contributes the following commands:

* `Google Java Format For VS Code: Clear Cache`: Clear cache of [Google Java Format executable](https://github.com/google/google-java-format/releases) downloads by the extension.
* `Google Java Format For VS Code: Reload Executable`: Reload the [Google Java Format executable](https://github.com/google/google-java-format/releases) using the current configuration.

## How to Debug

To debug this extension and see how exactly it invokes the formatter, use *Developer: Set Log Level...* to enable *Debug* for this extension, and then open the *Output* tab and select this extension.
Expand Down
32 changes: 30 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,26 @@
},
"java.format.settings.google.version": {
"type": "string",
"markdownDescription": "*Recommended.* Specifies version to be used of [Google Java Format jar executable](https://github.com/google/google-java-format/releases) in format `{major}.{minor}.{patch}`. Default: `latest`.",
"default": null,
"markdownDescription": "*Recommended.* Specifies version to be used of [Google Java Format executable](https://github.com/google/google-java-format/releases) in format `{major}.{minor}.{patch}`. Default: `latest`.",
"default": "latest",
"scope": "window"
},
"java.format.settings.google.mode": {
"type": "string",
"markdownDescription": "Specifies the runtime mode of [Google Java Format](https://github.com/google/google-java-format/releases). Used with `java.format.settings.google.version`",
"default": "native-binary",
"enum": [
"jar-file",
"native-binary"
],
"enumItemLabels": [
"Jar File",
"Native Binary"
],
"enumDescriptions": [
"Use Java runtime to execute jar file of Google Java Format.",
"Use Native Binary of Google Java Format, if available, otherwise, revert to Jar File."
],
"scope": "window"
},
"java.format.settings.google.extra": {
Expand All @@ -40,6 +58,16 @@
}
}
}
],
"commands": [
{
"command": "googleJavaFormatForVSCode.reloadExecutable",
"title": "Google Java Format For VS Code: Reload Executable"
},
{
"command": "googleJavaFormatForVSCode.clearCache",
"title": "Google Java Format For VS Code: Clear Cache"
}
]
},
"scripts": {
Expand Down
17 changes: 2 additions & 15 deletions release.config.cjs
Original file line number Diff line number Diff line change
@@ -1,13 +1,6 @@
/** @type {import('semantic-release').GlobalConfig} */
const config = {
branches: [
"+([0-9])?(.{+([0-9]),x}).x",
"main",
"next",
"next-major",
{ name: "beta", prerelease: "beta" },
{ name: "alpha", prerelease: "alpha" },
],
branches: ["main"],
plugins: [
[
"@semantic-release/commit-analyzer",
Expand All @@ -25,13 +18,7 @@ const config = {
},
],
["semantic-release-vsce", { packageVsix: true }],
[
"@semantic-release/git",
{
message:
"chore(release): ${nextRelease.version} [skip ci]\n\n${nextRelease.notes}",
},
],
"@semantic-release/git",
["@semantic-release/github", { assets: "*.vsix" }],
],
};
Expand Down
106 changes: 106 additions & 0 deletions src/Cache.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
import { createHash } from "node:crypto";
import path = require("node:path");
import {
ExtensionContext,
LogOutputChannel,
Uri,
commands,
workspace,
} from "vscode";

export class Cache {
private uri: Uri;

private constructor(
private context: ExtensionContext,
private log: LogOutputChannel,
cacheFolder: string,
) {
this.uri = Uri.joinPath(context.extensionUri, cacheFolder);
}

public static async getInstance(
context: ExtensionContext,
log: LogOutputChannel,
cacheFolder = "cache",
) {
const cache = new Cache(context, log, cacheFolder);
await cache.init();
return cache;
}

subscribe = () => {
this.context.subscriptions.push(
commands.registerCommand(
"googleJavaFormatForVSCode.clearCache",
this.clear,
),
);
};

clear = async () => {
// clear cache
await workspace.fs.delete(this.uri, { recursive: true });
this.log.info("Cache cleared.");
// reload executable after clearing cache
await commands.executeCommand(
"googleJavaFormatForVSCode.reloadExecutable",
);
};

private init = async () => {
// Create the cache directory if it doesn't exist
try {
await workspace.fs.createDirectory(this.uri);
this.log.debug(`Cache directory created at ${this.uri.toString()}`);
} catch (error) {
this.log.error(`Failed to create cache directory: ${error}`);
throw error;
}
};

get = async (url: string) => {
const basename = path.basename(url);

const hash = createHash("md5").update(url).digest("hex");
const dirname = Uri.joinPath(this.uri, hash);
const localPath = Uri.joinPath(dirname, basename);

try {
// Check if the file is already cached locally
await workspace.fs.stat(localPath);

this.log.info(`Using cached file at ${localPath.toString()}`);

return localPath;
} catch (error) {
// Create the cache directory if it doesn't exist
try {
await workspace.fs.createDirectory(dirname);
this.log.debug(
`Cache directory created at ${dirname.toString()}`,
);
} catch (error) {
this.log.error(`Failed to create cache directory: ${error}`);
throw error;
}

// Download the file and write it to the cache directory
this.log.info(`Downloading file from ${url}`);

const response = await fetch(url);
if (response.ok) {
const buffer = await response.arrayBuffer();
await workspace.fs.writeFile(localPath, new Uint8Array(buffer));

this.log.info(`File saved to ${localPath.toString()}`);

return localPath;
} else {
throw new Error(
`Failed to download file from ${url}: ${response.status} ${response.statusText}`,
);
}
}
};
}
132 changes: 132 additions & 0 deletions src/Executable.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
import { execSync } from "node:child_process";
import {
// CancellationToken,
ExtensionContext,
LogOutputChannel,
// Progress,
ProgressLocation,
commands,
window,
} from "vscode";
import { Cache } from "./Cache";
import { ExtensionConfiguration } from "./ExtensionConfiguration";
import { resolveExecutableFileFromConfig } from "./resolveExecutableFileFromConfig";
import path = require("node:path");

export class Executable {
private runner: string = null!;
private cwd: string = null!;

private constructor(
private context: ExtensionContext,
private config: ExtensionConfiguration,
private cache: Cache,
private log: LogOutputChannel,
) {}

public static async getInstance(
context: ExtensionContext,
config: ExtensionConfiguration,
cache: Cache,
log: LogOutputChannel,
) {
const instance = new Executable(context, config, cache, log);
await instance.load();
return instance;
}

run = async ({
args,
stdin,
}: {
args: string[];
stdin: string;
signal?: AbortSignal;
}) => {
return new Promise<string>((resolve, reject) => {
try {
const command = `${this.runner} ${args.join(" ")} -`;

this.log.debug(`> ${command}`);

const stdout: string = execSync(command, {
cwd: this.cwd,
encoding: "utf8",
input: stdin,
maxBuffer: Infinity,
windowsHide: true,
});

resolve(stdout);
} catch (e) {
reject(e);
}
});
};

subscribe = () => {
this.config.subscriptions.push(this.configurationChangeListener);
this.context.subscriptions.push(
commands.registerCommand(
"googleJavaFormatForVSCode.reloadExecutable",
this.load,
),
);
};

private load = async () =>
// progress?: Progress<{
// message?: string | undefined;
// increment?: number | undefined;
// }>,
// token?: CancellationToken,
{
const uri = await resolveExecutableFileFromConfig(
this.config,
this.log,
);

const { fsPath } =
uri.scheme === "file"
? uri
: await this.cache.get(uri.toString());

const isJar = fsPath.endsWith(".jar");
const dirname = path.dirname(fsPath);
const basename = path.basename(fsPath);

if (isJar) {
this.runner = `java -jar ./${basename}`;
} else if (process.platform === "win32") {
this.runner = basename;
} else {
this.runner = `./${basename}`;
}

this.cwd = dirname;
};

private configurationChangeListener = async () => {
this.log.info("Configuration change detected.");
const action = await window.showInformationMessage(
"Configuration change detected. Update executable?",
"Update",
"Ignore",
);

if (action !== "Update") {
this.log.debug("User ignored updating executable.");
return;
}

this.log.debug("Updating executable...");
window.withProgress(
{
location: ProgressLocation.Notification,
title: "Updating executable...",
cancellable: false,
},
this.load,
);
};
}
Loading

0 comments on commit f783888

Please sign in to comment.