diff --git a/server/src/e2e/suite/inlayHints.test.ts b/server/src/e2e/suite/inlayHints.test.ts new file mode 100644 index 00000000..6d7d6a2e --- /dev/null +++ b/server/src/e2e/suite/inlayHints.test.ts @@ -0,0 +1,66 @@ +import * as vscode from "vscode" +import * as assert from "node:assert" +import {BaseTestSuite} from "./BaseTestSuite" +import type {TestCase} from "./TestParser" + +suite("Inlay Hints Test Suite", () => { + const testSuite = new (class extends BaseTestSuite { + public async getHints(input: string): Promise { + await this.replaceDocumentText(input) + return vscode.commands.executeCommand( + "vscode.executeInlayHintProvider", + this.document.uri, + new vscode.Range( + this.document.positionAt(0), + this.document.positionAt(input.length), + ), + ) + } + + protected runTest(testFile: string, testCase: TestCase): void { + test(`Hint: ${testCase.name}`, async () => { + const hints = await this.getHints(testCase.input) + const expected = testCase.expected.split("\n").filter((line: string) => line !== "") + await this.replaceDocumentText(testCase.input) + + for (let x = 0; x < hints.length; x++) { + const hint = hints[x] + + await this.editor.edit(editBuilder => { + if (typeof hint.label === "string") { + editBuilder.insert(hint.position, `/* ${hint.label} */`) + } + }) + + const updatedHints = await this.getHints(this.document.getText()) + if (x + 1 < hints.length) { + if (x + 1 < updatedHints.length) { + hints[x + 1].position = updatedHints[x + 1].position + } + } + } + + if (BaseTestSuite.UPDATE_SNAPSHOTS) { + this.updates.push({ + filePath: testFile, + testName: testCase.name, + actual: this.document.getText(), + }) + } else { + assert.deepStrictEqual(hints, expected) + } + }) + } + })() + + suiteSetup(async function () { + this.timeout(10_000) + await testSuite.suiteSetup() + }) + + setup(async () => testSuite.setup()) + teardown(async () => testSuite.teardown()) + suiteTeardown(() => testSuite.suiteTeardown()) + + testSuite.runTestsFromDirectory("inlayHints") +}) diff --git a/server/src/e2e/suite/testcases/inlayHints/assembly.test b/server/src/e2e/suite/testcases/inlayHints/assembly.test new file mode 100644 index 00000000..87bc2851 --- /dev/null +++ b/server/src/e2e/suite/testcases/inlayHints/assembly.test @@ -0,0 +1,42 @@ +======================================================================== +Show gas consumption hints for asm functions +======================================================================== +primitive Int; + +asm fun sum(two: AB): Int { + ADD +} +------------------------------------------------------------------------ +primitive Int; + +asm fun sum(two: AB): Int { + ADD/* 18 */ +} + +======================================================================== +Show gas consumption hints for asm functions (double) +======================================================================== +primitive Int; + +asm fun push_two(x: Int) { + INC + INC +} +------------------------------------------------------------------------ +primitive Int; + +asm fun push_two(x: Int) {/* 36 gas */ + INC/* 18 */ + INC/* 18 */ +} + +======================================================================== +Non-existent TVM instruction +======================================================================== +primitive Int; + +asm fun bocchiThe(BOC: Cell): Cell { BOC } +------------------------------------------------------------------------ +primitive Int; + +asm fun bocchiThe(BOC: Cell): Cell { BOC } diff --git a/server/src/e2e/suite/testcases/inlayHints/contract.test b/server/src/e2e/suite/testcases/inlayHints/contract.test new file mode 100644 index 00000000..1589edda --- /dev/null +++ b/server/src/e2e/suite/testcases/inlayHints/contract.test @@ -0,0 +1,153 @@ +======================================================================== +Simple Contract +======================================================================== +primitive Int; + +contract Foo { + Bar: Int; +} +------------------------------------------------------------------------ +primitive Int; + +contract Foo { + Bar: Int/* as int257 */; +} + +======================================================================== +Contract with some optionals +======================================================================== +primitive Int; + +contract Foo { + Bar: Int; + Baz: Int?; + balance: uint32 as coins; + Start: Int = 0; +} +------------------------------------------------------------------------ +primitive Int; + +contract Foo { + Bar: Int/* as int257 */; + Baz: Int?; + balance: uint32 as coins; + Start: Int/* as int257 */ = 0; +} + +======================================================================== +Show method ID in contract +======================================================================== +primitive Int; + +contract Foo { + get fun bar(){ + const a: Int; + } +} +------------------------------------------------------------------------ +primitive Int; + +contract Foo { + get/* (0x1ab79) */ fun bar(){ + const a: Int; + } +} + +======================================================================== +Exit codes +======================================================================== +primitive Int; + +contract Foo { + receive(msg: "OK") { + try { + const Bar: Int; + } catch (exitCode) { + // something more + } + } +} +------------------------------------------------------------------------ +primitive Int; + +contract Foo { + receive(msg: "OK") { + try { + const Bar: Int; + } catch (exitCode/* : Int */) { + // something more + } + } +} + +======================================================================== +Show exit codes in require() call +======================================================================== +primitive String; +primitive Bool; + +fun require(that: Bool, msg: String) {} + +contract Foo { + + receive(msg: "Test") { + require(true, "bar"); + } +} +------------------------------------------------------------------------ +primitive String; +primitive Bool; + +fun require(that: Bool, msg: String) {} + +contract Foo { + + receive(msg: "Test") { + require(/* that: */true, /* msg: */"bar"/* exit code: 62478 */); + } +} + +======================================================================== +Show parameter name hints in function calls +======================================================================== +contract Foo { + + internal fun test1(x: Int) { + return x; + } + + external fun test2(x: Int) { + return x; + } + + get fun test3(x: Int) { + return x; + } + + init() { + const a = self.test1(42); + const b = self.test2(42); + const c = self.test3(42); + } +} +------------------------------------------------------------------------ +contract Foo { + + internal fun test1(x: Int) { + return x; + } + + external fun test2(x: Int) { + return x; + } + + get/* (0x1091c) */ fun test3(x: Int) { + return x; + } + + init() { + const a = self.test1(/* x: */42); + const b = self.test2(/* x: */42); + const c = self.test3(/* x: */42); + } +} diff --git a/server/src/e2e/suite/testcases/inlayHints/message.test b/server/src/e2e/suite/testcases/inlayHints/message.test new file mode 100644 index 00000000..1445d9d9 --- /dev/null +++ b/server/src/e2e/suite/testcases/inlayHints/message.test @@ -0,0 +1,61 @@ +======================================================================== +Single Message +======================================================================== +primitive Int; +message Foo { + a: Int; +} +------------------------------------------------------------------------ +primitive Int; +message Foo { + a: Int/* as int257 */; +} + +======================================================================== +Message with many types +======================================================================== +primitive Int; +message Foo { + a: Int; + b: Int; +} +------------------------------------------------------------------------ +primitive Int; +message Foo { + a: Int/* as int257 */; + b: Int/* as int257 */; +} + +======================================================================== +Message with uint* +======================================================================== +primitive Int; +message Foo { + a: Int; + b: Int; + c: Int as uint32; + d: Int as coins; +} +------------------------------------------------------------------------ +primitive Int; +message Foo { + a: Int/* as int257 */; + b: Int/* as int257 */; + c: Int as uint32; + d: Int as coins; +} + +======================================================================== +No hint for optional +======================================================================== +primitive Int; +message Foo { + a: Int?; + b: Address?; +} +------------------------------------------------------------------------ +primitive Int; +message Foo { + a: Int?; + b: Address?; +} diff --git a/server/src/e2e/suite/testcases/inlayHints/struct.test b/server/src/e2e/suite/testcases/inlayHints/struct.test new file mode 100644 index 00000000..9af619a2 --- /dev/null +++ b/server/src/e2e/suite/testcases/inlayHints/struct.test @@ -0,0 +1,61 @@ +======================================================================== +Single Struct +======================================================================== +primitive Int; +struct Foo { + a: Int; +} +------------------------------------------------------------------------ +primitive Int; +struct Foo { + a: Int/* as int257 */; +} + +======================================================================== +Struct with many types +======================================================================== +primitive Int; +struct Foo { + a: Int; + b: Int; +} +------------------------------------------------------------------------ +primitive Int; +struct Foo { + a: Int/* as int257 */; + b: Int/* as int257 */; +} + +======================================================================== +Struct with uint* +======================================================================== +primitive Int; +struct Foo { + a: Int; + b: Int; + c: Int as uint32; + d: Int as coins; +} +------------------------------------------------------------------------ +primitive Int; +struct Foo { + a: Int/* as int257 */; + b: Int/* as int257 */; + c: Int as uint32; + d: Int as coins; +} + +======================================================================== +No hint for optional +======================================================================== +primitive Int; +struct Foo { + a: Int?; + b: Address?; +} +------------------------------------------------------------------------ +primitive Int; +struct Foo { + a: Int?; + b: Address?; +} diff --git a/server/src/e2e/suite/testcases/inlayHints/trait.test b/server/src/e2e/suite/testcases/inlayHints/trait.test new file mode 100644 index 00000000..efa23c1f --- /dev/null +++ b/server/src/e2e/suite/testcases/inlayHints/trait.test @@ -0,0 +1,35 @@ +======================================================================== +Simple Trait +======================================================================== +primitive Int; + +trait Foo { + Bar: Int; +} +------------------------------------------------------------------------ +primitive Int; + +trait Foo { + Bar: Int/* as int257 */; +} + +======================================================================== +Trait with some optionals +======================================================================== +primitive Int; + +trait Foo { + Bar: Int; + Baz: Int?; + Balance: uint32 as coins; + Start: Int = 0; +} +------------------------------------------------------------------------ +primitive Int; + +trait Foo { + Bar: Int/* as int257 */; + Baz: Int?; + Balance: uint32 as coins; + Start: Int/* as int257 */ = 0; +}