Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add forge fmt option #239

Merged
merged 2 commits into from
Sep 9, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions client/src/formatter/forgeFormatter.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import * as vscode from "vscode";
import * as cp from "child_process";

export async function formatDocument(
document: vscode.TextDocument
): Promise<vscode.TextEdit[]> {
const firstLine = document.lineAt(0);
const lastLine = document.lineAt(document.lineCount - 1);
const fullTextRange = new vscode.Range(
firstLine.range.start,
lastLine.range.end
);

const formatted = await new Promise<string>((resolve, reject) => {
const forge = cp.execFile("forge", ["fmt", "--raw", "-"], (err, stdout) => {
if (err !== null) {
return reject(err);
}

resolve(stdout);
});

forge.stdin?.write(document.getText());
forge.stdin?.end();
});

return [vscode.TextEdit.replace(fullTextRange, formatted)];
}
109 changes: 16 additions & 93 deletions client/src/formatter/index.ts
Original file line number Diff line number Diff line change
@@ -1,102 +1,25 @@
import * as prettier from "prettier";
import * as vscode from "vscode";
import * as path from "path";
import * as prettierPluginSolidity from "prettier-plugin-solidity";
import * as prettier from "./prettierFormatter";
import * as forge from "./forgeFormatter";

export function formatDocument(
export async function formatDocument(
document: vscode.TextDocument,
context: vscode.ExtensionContext
): vscode.TextEdit[] {
const rootPath = getCurrentWorkspaceRootFsPath();

if (rootPath === undefined) {
return [];
}

const ignoreOptions = {
ignorePath: path.join(rootPath, ".prettierignore"),
};

const fileInfo = prettier.getFileInfo.sync(
document.uri.fsPath,
ignoreOptions
);

if (fileInfo.ignored) {
return [];
}

const source = document.getText();

const options = {
useCache: false,
parser: "solidity-parse",
pluginSearchDirs: [context.extensionPath],
plugins: [prettierPluginSolidity],
};

const config =
prettier.resolveConfig.sync(document.uri.fsPath, options) ??
defaultConfig();

Object.assign(options, config);

const firstLine = document.lineAt(0);
const lastLine = document.lineAt(document.lineCount - 1);

const fullTextRange = new vscode.Range(
firstLine.range.start,
lastLine.range.end
);

): Promise<vscode.TextEdit[]> {
try {
const formatted = prettier.format(source, options);

return [vscode.TextEdit.replace(fullTextRange, formatted)];
const formatter = vscode.workspace
.getConfiguration("hardhat")
.get("formatter");

switch (formatter) {
case "prettier":
return await prettier.formatDocument(document, context);
case "forge":
return await forge.formatDocument(document);
default:
return [];
}
} catch {
// ignore formatter errors, we get lots of fails
return [];
}
}

function getCurrentWorkspaceRootFsPath(): string | undefined {
const rootFolder = getCurrentWorkspaceRootFolder();

return rootFolder?.uri.fsPath;
}

function getCurrentWorkspaceRootFolder(): vscode.WorkspaceFolder | undefined {
const editor = vscode.window.activeTextEditor;

if (!editor) {
return undefined;
}

const currentDocument = editor.document.uri;

return vscode.workspace.getWorkspaceFolder(currentDocument);
}

function defaultConfig() {
return {
printWidth: 80,
tabWidth: 4,
useTabs: false,
singleQuote: false,
bracketSpacing: false,
explicitTypes: "preserve",
overrides: [
{
files: "*.sol",
options: {
printWidth: 80,
tabWidth: 4,
useTabs: false,
singleQuote: false,
bracketSpacing: false,
explicitTypes: "preserve",
},
},
],
};
}
94 changes: 94 additions & 0 deletions client/src/formatter/prettierFormatter.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
import * as prettier from "prettier";
import * as vscode from "vscode";
import * as path from "path";
import * as prettierPluginSolidity from "prettier-plugin-solidity";

export async function formatDocument(
document: vscode.TextDocument,
context: vscode.ExtensionContext
): Promise<vscode.TextEdit[]> {
const rootPath = getCurrentWorkspaceRootFsPath();

if (rootPath === undefined) {
return [];
}

const ignoreOptions = {
ignorePath: path.join(rootPath, ".prettierignore"),
};

const fileInfo = prettier.getFileInfo.sync(
document.uri.fsPath,
ignoreOptions
);

if (fileInfo.ignored) {
return [];
}

const source = document.getText();

const options = {
useCache: false,
parser: "solidity-parse",
pluginSearchDirs: [context.extensionPath],
plugins: [prettierPluginSolidity],
};

const config = await prettier.resolveConfig(document.uri.fsPath, options);
Object.assign(options, config ?? defaultConfig());

const firstLine = document.lineAt(0);
const lastLine = document.lineAt(document.lineCount - 1);

const fullTextRange = new vscode.Range(
firstLine.range.start,
lastLine.range.end
);

const formatted = prettier.format(source, options);

return [vscode.TextEdit.replace(fullTextRange, formatted)];
}

function getCurrentWorkspaceRootFsPath(): string | undefined {
const rootFolder = getCurrentWorkspaceRootFolder();

return rootFolder?.uri.fsPath;
}

function getCurrentWorkspaceRootFolder(): vscode.WorkspaceFolder | undefined {
const editor = vscode.window.activeTextEditor;

if (!editor) {
return undefined;
}

const currentDocument = editor.document.uri;

return vscode.workspace.getWorkspaceFolder(currentDocument);
}

function defaultConfig() {
return {
printWidth: 80,
tabWidth: 4,
useTabs: false,
singleQuote: false,
bracketSpacing: false,
explicitTypes: "preserve",
overrides: [
{
files: "*.sol",
options: {
printWidth: 80,
tabWidth: 4,
useTabs: false,
singleQuote: false,
bracketSpacing: false,
explicitTypes: "preserve",
},
},
],
};
}
6 changes: 4 additions & 2 deletions client/src/setup/setupFormatterHook.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,11 @@ export function setupFormatterHook({
}) {
context.subscriptions.push(
languages.registerDocumentFormattingEditProvider("solidity", {
provideDocumentFormattingEdits(document: TextDocument): TextEdit[] {
async provideDocumentFormattingEdits(
document: TextDocument
): Promise<TextEdit[]> {
try {
return formatDocument(document, context);
return await formatDocument(document, context);
} catch (err: unknown) {
logger.error(err);
return [];
Expand Down
10 changes: 10 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,16 @@
"type": "boolean",
"markdownDescription": "Allow **Hardhat for Visual Studio Code** to send extension telemetry. This helps us understand how **Hardhat for Visual Studio Code** is used and how it is performing. Read more in our [privacy policy](https://hardhat.org/privacy-policy.html).\n\n&nbsp;\n\n*__Note:__ **Hardhat for Visual Studio Code** respects the global **Telemetry Level** setting, and will only send telemetry if enabled at both global and extension level.*",
"default": false
},
"hardhat.formatter": {
"type": "string",
"default": "prettier",
"enum": [
"none",
"prettier",
"forge"
],
"description": "Enables / disables the solidity formatter (prettier solidity default)"
}
}
},
Expand Down