Skip to content

Commit

Permalink
feat: auto insert semicolon on import completion (#104)
Browse files Browse the repository at this point in the history
  • Loading branch information
antico5 committed Sep 9, 2022
1 parent c2ab899 commit f07fa2e
Show file tree
Hide file tree
Showing 27 changed files with 85 additions and 39 deletions.
2 changes: 1 addition & 1 deletion client/src/commands/Command.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { ExtensionState } from "../types";
export default abstract class Command {
constructor(public state: ExtensionState) {}

public abstract execute(commandArgs: unknown): Promise<unknown>;
public abstract execute(...commandArgs: unknown[]): Promise<unknown>;

public abstract name(): string;
}
24 changes: 24 additions & 0 deletions client/src/commands/InsertSemicolonCommand.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import vscode, { Position } from "vscode";
import Command from "./Command";

export default class InsertSemicolonCommand extends Command {
public async execute(position: Position) {
const editor = vscode.window.activeTextEditor;

if (editor === undefined) return;

const document = editor.document;

const lineText = document.lineAt(position.line).text;

if (!/.*;$/.test(lineText)) {
await editor.edit((builder) =>
builder.insert(new Position(position.line, lineText.length), ";")
);
}
}

public name(): string {
return "insertSemicolon";
}
}
9 changes: 7 additions & 2 deletions client/src/setup/setupCommands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,24 @@ import { ExtensionState } from "../types";
import CompileCommand from "../commands/CompileCommand";
import FlattenCurrentFileCommand from "../commands/FlattenCurrentFileCommand";
import CleanCommand from "../commands/CleanCommand";
import InsertSemicolonCommand from "../commands/InsertSemicolonCommand";
import Command from "../commands/Command";

const commandClasses = [
type ICommandClass = new (state: ExtensionState) => Command;

const commandClasses: ICommandClass[] = [
CompileCommand,
FlattenCurrentFileCommand,
CleanCommand,
InsertSemicolonCommand,
];

export async function setupCommands(state: ExtensionState) {
for (const commandClass of commandClasses) {
const command = new commandClass(state);
const disposable = vscode.commands.registerCommand(
`hardhat.solidity.${command.name()}`,
() => command.execute()
(...args) => command.execute(...args)
);
state.context.subscriptions.push(disposable);
}
Expand Down
5 changes: 5 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,11 @@
"command": "hardhat.solidity.clean",
"title": "Hardhat: Clear cache and artifacts "
},
{
"command": "hardhat.solidity.insertSemicolon",
"title": "Insert semicolon",
"enablement": "false"
},
{
"command": "hardhat.solidity.flattenCurrentFile",
"title": "Hardhat: Flatten this file and its dependencies",
Expand Down
9 changes: 9 additions & 0 deletions server/src/services/completion/onCompletion.ts
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,15 @@ export function doComplete(
result.items = getImportPathCompletion(position, definitionNode, projCtx, {
logger,
});

// Trigger auto-insertion of semicolon after import completion
for (const item of result.items) {
item.command = {
command: "hardhat.solidity.insertSemicolon",
arguments: [position],
title: "",
};
}
} else if (
definitionNode &&
expressionNodeTypes.includes(definitionNode.getExpressionNode()?.type ?? "")
Expand Down
2 changes: 1 addition & 1 deletion server/src/services/initialization/onInitialize.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ export const onInitialize = (serverState: ServerState) => {
textDocumentSync: TextDocumentSyncKind.Incremental,
// Tell the client that this server supports code completion.
completionProvider: {
triggerCharacters: [".", "/", '"'],
triggerCharacters: [".", "/", '"', "'"],
},
signatureHelpProvider: {
triggerCharacters: ["(", ","],
Expand Down
2 changes: 0 additions & 2 deletions server/test/services/completion/imports.ts
Original file line number Diff line number Diff line change
Expand Up @@ -591,8 +591,6 @@ describe("Parser", () => {
});

describe("direct", function () {
this.timeout(5000);

before(async () => {
const openzepplinUri1 = forceToUnixStyle(
path.join(
Expand Down
8 changes: 8 additions & 0 deletions test/integration/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,20 @@ import path from "path";
import Mocha from "mocha";
// eslint-disable-next-line import/no-extraneous-dependencies
import glob from "glob";
import { sleep } from "./helpers/sleep";

export function run(): Promise<void> {
// Create the mocha test
const mocha = new Mocha({
ui: "tdd",
color: true,
rootHooks: {
beforeAll: async () => {
await sleep(5000); // Wait for the extension to be loaded
},
},
timeout: 30000,
retries: 1,
});

const testsRoot = path.resolve(__dirname, "tests");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,6 @@ import {
} from "../../helpers/editor";

suite("codeactions - add license identifier", function () {
this.timeout(30000);

test("add MIT license identifier", async () => {
const uri = getDocUri(__dirname, "./NoLicense.sol");
const editor = await openFileInEditor(uri);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,6 @@ import {
} from "../../helpers/editor";

suite("codeactions - add multi override specifier", function () {
this.timeout(30000);

test("add multi override on multiple occurrences", async () => {
const uri = getDocUri(__dirname, "./AddMultioverrideSpecifier.sol");
const editor = await openFileInEditor(uri);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,6 @@ import {
} from "../../helpers/editor";

suite("codeactions - add override specifier", function () {
this.timeout(30000);

test("add override on multiple occurrences", async () => {
const uri = getDocUri(__dirname, "./AddOverrideSpecifier.sol");
const editor = await openFileInEditor(uri);
Expand Down
2 changes: 0 additions & 2 deletions test/integration/tests/codeactions/addPragmaVersion.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,6 @@ import {
} from "../../helpers/editor";

suite("codeactions - add solidity pragma", function () {
this.timeout(30000);

test("add version of current compiler", async () => {
const uri = getDocUri(__dirname, "./NoPragma.sol");
const editor = await openFileInEditor(uri);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,6 @@ import {
} from "../../helpers/editor";

suite("codeactions - add virtual specifier", function () {
this.timeout(30000);

test("add virtual specifier on multiple occurrences", async () => {
const uri = getDocUri(__dirname, "./AddVirtualSpecifier.sol");
const editor = await openFileInEditor(uri);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,6 @@ import {
} from "../../helpers/editor";

suite("codeactions - constrain mutability", function () {
this.timeout(30000);

test("add view modifier", async () => {
const uri = getDocUri(__dirname, "./ConstrainMutabilityView.sol");
const editor = await openFileInEditor(uri);
Expand Down
2 changes: 0 additions & 2 deletions test/integration/tests/codeactions/markAbstract.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,6 @@ import {
} from "../../helpers/editor";

suite("codeactions - mark abstract", function () {
this.timeout(30000);

test("add missing functions from interfaces", async () => {
const uri = getDocUri(__dirname, "./MarkAbstract.sol");
const editor = await openFileInEditor(uri);
Expand Down
2 changes: 0 additions & 2 deletions test/integration/tests/codeactions/specifyVisibility.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,6 @@ import {
} from "../../helpers/editor";

suite("codeactions - specify visibility", function () {
this.timeout(30000);

test("specify public", async () => {
const uri = getDocUri(__dirname, "./SpecifyVisibility.sol");
const editor = await openFileInEditor(uri);
Expand Down
2 changes: 0 additions & 2 deletions test/integration/tests/commands/clean.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,6 @@ import { sleep } from "../../helpers/sleep";
import { getRootPath } from "../../helpers/workspace";

suite("commands - clean", function () {
this.timeout(30000);

test("clean via command palette", async () => {
// Close any active editor - since running a task saves the current file apparently
await vscode.commands.executeCommand("workbench.action.closeActiveEditor");
Expand Down
2 changes: 0 additions & 2 deletions test/integration/tests/commands/flatten.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,6 @@ import { getDocUri } from "../../helpers/docPaths";
import { openFileInEditor, waitForUI } from "../../helpers/editor";

suite("commands - flatten", function () {
this.timeout(30000);

test("flatten via command palette", async () => {
const uri = getDocUri(__dirname, "./Importer.sol");
await openFileInEditor(uri);
Expand Down
29 changes: 29 additions & 0 deletions test/integration/tests/completion/completion.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import * as assert from "assert";
import vscode from "vscode";
import { openFileInEditor, waitForUI } from "../../helpers/editor";
import { type } from "../../helpers/commands";
import { getDocUri } from "../../helpers/docPaths";

suite.only("completion", function () {
test("[completion] - add semicolon automatically after import", async () => {
const uri = getDocUri(__dirname, "./AddSemicolon.sol");
const editor = await openFileInEditor(uri);
const document = editor.document;

await type(document, "import '");
await waitForUI();
await vscode.commands.executeCommand("acceptSelectedSuggestion");
await waitForUI();
assert.equal(document.getText(), "import '@openzeppelin';");

await type(document, "/");
await waitForUI();
await vscode.commands.executeCommand("acceptSelectedSuggestion");

await waitForUI();
assert.equal(
document.getText(),
"import '@openzeppelin/contracts/access/AccessControl.sol';"
);
});
});
Empty file.
2 changes: 1 addition & 1 deletion test/integration/tests/configuration/configuration.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ suite("Configuration", function () {
textDocumentSync: 2,
codeActionProvider: true,
completionProvider: {
triggerCharacters: [".", "/", '"'],
triggerCharacters: [".", "/", '"', "'"],
},
signatureHelpProvider: {
triggerCharacters: ["(", ","],
Expand Down
2 changes: 0 additions & 2 deletions test/integration/tests/definition/definition.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,6 @@ import { assertLspCommand } from "../../common/assertLspCommand";
import { getDocUri } from "../../helpers/docPaths";

suite("Single-file Navigation", function () {
this.timeout(10000);

const testUri = getDocUri(__dirname, "./Test.sol");
const importTestUri = getDocUri(__dirname, "./ImportTest.sol");

Expand Down
2 changes: 0 additions & 2 deletions test/integration/tests/diagnostics/diagnostics.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@ import { getDocUri } from "../../helpers/docPaths";
import { openFileInEditor } from "../../helpers/editor";

suite("diagnostics", function () {
this.timeout(30000);

test("[diagnostics] missing semicolon", async () => {
const uri = getDocUri(__dirname, "./MissingSemicolon.sol");
await openFileInEditor(uri);
Expand Down
2 changes: 0 additions & 2 deletions test/integration/tests/implementation/implementation.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@ import { assertLspCommand } from "../../common/assertLspCommand";
import { getDocUri } from "../../helpers/docPaths";

suite("Single-file Navigation", function () {
this.timeout(10000);

const testUri = getDocUri(__dirname, "./Test.sol");

let client!: Client;
Expand Down
2 changes: 0 additions & 2 deletions test/integration/tests/references/references.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@ import { assertLspCommand } from "../../common/assertLspCommand";
import { getDocUri } from "../../helpers/docPaths";

suite("Single-file Navigation", function () {
this.timeout(10000);

const testUri = getDocUri(__dirname, "./Test.sol");
const importedUri = getDocUri(__dirname, "./Imported.sol");
const modifierInvocationUri = getDocUri(
Expand Down
2 changes: 0 additions & 2 deletions test/integration/tests/rename/rename.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@ import { assertLspCommand } from "../../common/assertLspCommand";
import { getDocUri } from "../../helpers/docPaths";

suite("Single-file Navigation", function () {
this.timeout(10000);

const testUri = getDocUri(__dirname, "./Test.sol");
const importTestUri = getDocUri(__dirname, "./MultiImport.sol");

Expand Down
2 changes: 0 additions & 2 deletions test/integration/tests/typedefinition/typedefinition.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@ import { assertLspCommand } from "../../common/assertLspCommand";
import { getDocUri } from "../../helpers/docPaths";

suite("Single-file Navigation", function () {
this.timeout(10000);

const testUri = getDocUri(__dirname, "./Test.sol");
const importedUri = getDocUri(__dirname, "./Imported.sol");

Expand Down

0 comments on commit f07fa2e

Please sign in to comment.