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

Language server v0.4.0 #154

Merged
merged 3 commits into from
Jun 3, 2020
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
400 changes: 12 additions & 388 deletions languageserver/go.sum

Large diffs are not rendered by default.

18 changes: 0 additions & 18 deletions tools/vscode-extension/out/test/extension.test.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

18 changes: 0 additions & 18 deletions tools/vscode-extension/out/test/index.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

14 changes: 7 additions & 7 deletions tools/vscode-extension/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"displayName": "Cadence",
"publisher": "dapperlabs",
"description": "",
"version": "0.1.0",
"version": "0.4.0",
"repository": {
"type": "git",
"url": "https://github.com/onflow/cadence.git"
Expand Down Expand Up @@ -56,22 +56,22 @@
"description": "The command to invoke the Flow CLI",
"scope": "resource"
},
"cadence.rootPrivateKey": {
"cadence.servicePrivateKey": {
"type": "string",
"default": "bf9db4706c2fdb9011ee7e170ccac492f05427b96ab41d8bf2d8c58443704b76",
"description": "The private key of the root account to use when submitting transactions",
"description": "The private key of the service account to use when submitting transactions",
"scope": "resource"
},
"cadence.rootKeySignatureAlgorithm": {
"cadence.serviceKeySignatureAlgorithm": {
"type": "string",
"default": "ECDSA_P256",
"description": "The signature algorithm of the root account to use when submitting transactions",
"description": "The signature algorithm of the service account to use when submitting transactions",
"scope": "resource"
},
"cadence.rootKeyHashAlgorithm": {
"cadence.serviceKeyHashAlgorithm": {
"type": "string",
"default": "SHA3_256",
"description": "The hash algorithm of the root account to use when submitting transactions",
"description": "The hash algorithm of the service account to use when submitting transactions",
"scope": "resource"
},
"cadence.emulatorAddress": {
Expand Down
14 changes: 5 additions & 9 deletions tools/vscode-extension/src/address.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,14 @@
const addressPrefix = "0x";

// Format an address by trimming leading zeros and enforcing an even length.
export function shortAddress(address: string): string {
const addressInt = parseInt(address, 16);

let addressStr = addressInt.toString(16);
if (addressStr.length % 2 !== 0) {
addressStr = `0${addressStr}`;
export function addAddressPrefix(address: string): string {
if (address.slice(0, 2) === addressPrefix) {
return address;
}

return `${addressPrefix}${addressStr}`;
return addressPrefix + address;
}

export function stripAddressPrefix(address: string): string {
export function removeAddressPrefix(address: string): string {
if (address.slice(0, 2) === addressPrefix) {
return address.slice(2);
}
Expand Down
45 changes: 28 additions & 17 deletions tools/vscode-extension/src/commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import {commands, ExtensionContext, Position, Range, window, workspace} from "vs
import {Extension, renderExtension} from "./extension";
import {LanguageServerAPI} from "./language-server";
import {createTerminal} from "./terminal";
import {shortAddress, stripAddressPrefix} from "./address";
import {removeAddressPrefix} from "./address";

// Command identifiers for locally handled commands
export const RESTART_SERVER = "cadence.restartServer";
Expand Down Expand Up @@ -39,22 +39,22 @@ const restartServer = (ext: Extension) => async () => {

// Starts the emulator in a terminal window.
const startEmulator = (ext: Extension) => async () => {
// Start the emulator with the root key we gave to the language server.
// Start the emulator with the service key we gave to the language server.
const {serverConfig} = ext.config

ext.terminal.sendText(
[
ext.config.flowCommand,
`emulator`, `start`, `--init`, `--verbose`,
`--root-priv-key`, serverConfig.rootPrivateKey,
`--root-sig-algo`, serverConfig.rootKeySignatureAlgorithm,
`--root-hash-algo`, serverConfig.rootKeyHashAlgorithm,
`--service-priv-key`, serverConfig.servicePrivateKey,
`--service-sig-algo`, serverConfig.serviceKeySignatureAlgorithm,
`--service-hash-algo`, serverConfig.serviceKeyHashAlgorithm,
].join(" ")
);
ext.terminal.show();

// create default accounts after the emulator has started
// skip root account since it is already created
// skip service account since it is already created
setTimeout(async () => {
try {
const accounts = await ext.api.createDefaultAccounts(ext.config.numAccounts - 1);
Expand Down Expand Up @@ -98,9 +98,17 @@ const switchActiveAccount = (ext: Extension) => async () => {
const activeSuffix = "(active)";
// Create the options (mark the active account with an 'active' prefix)
const accountOptions = Object
.keys(ext.config.accounts)
.values(ext.config.accounts)
// Mark the active account with a `*` in the dialog
.map(addr => addr === ext.config.activeAccount ? `${shortAddress(addr)} ${activeSuffix}` : shortAddress(addr));
.map((account) => {
const suffix: String = account.index === ext.config.activeAccount ? ` ${activeSuffix}` : "";
const label = `${account.fullName()}${suffix}`;

return {
label: label,
target: account.index
}
})

window.showQuickPick(accountOptions)
.then(selected => {
Expand All @@ -109,17 +117,17 @@ const switchActiveAccount = (ext: Extension) => async () => {
if (selected === undefined) {
return;
}
// If the user selected the active account, remove the `*` prefix
if (selected.endsWith(activeSuffix)) {
selected = selected.slice(0, -activeSuffix.length).trim();
}
if (!ext.config.accounts[selected]) {
console.error('Switched to invalid account: ', selected);

const activeIndex = selected.target;
const activeAccount = ext.config.getAccount(activeIndex);

if (!activeAccount) {
console.error('Switched to invalid account');
return;
}

try {
ext.api.switchActiveAccount(stripAddressPrefix(selected));
ext.api.switchActiveAccount(removeAddressPrefix(activeAccount.address));
window.visibleTextEditors.forEach(editor => {
if (!editor.document.lineCount) {
return;
Expand All @@ -142,8 +150,11 @@ const switchActiveAccount = (ext: Extension) => async () => {
console.error(err);
return;
}
ext.config.activeAccount = selected;
window.showInformationMessage(`Switched to account ${selected}`);

ext.config.setActiveAccount(activeIndex)

window.showInformationMessage(`Switched to account ${activeAccount.fullName()}`);

renderExtension(ext);
});
};
96 changes: 62 additions & 34 deletions tools/vscode-extension/src/config.ts
Original file line number Diff line number Diff line change
@@ -1,59 +1,87 @@
import {commands, window, workspace} from "vscode";
import {shortAddress} from "./address";
import { addAddressPrefix } from "./address";

export const ROOT_ADDR: string = shortAddress("0000000000000000000000000000000000000001");
export const SERVICE_ADDR: string = "f8d6e0586b0a20c7";

const CONFIG_FLOW_COMMAND = "flowCommand";
const CONFIG_ROOT_PRIVATE_KEY = "rootPrivateKey";
const CONFIG_ROOT_KEY_SIGNATURE_ALGORITHM = "rootKeySignatureAlgorithm";
const CONFIG_ROOT_KEY_HASH_ALGORITHM = "rootKeyHashAlgorithm";
const CONFIG_SERVICE_PRIVATE_KEY = "servicePrivateKey";
const CONFIG_SERVICE_KEY_SIGNATURE_ALGORITHM = "serviceKeySignatureAlgorithm";
const CONFIG_SERVICE_KEY_HASH_ALGORITHM = "serviceKeyHashAlgorithm";
const CONFIG_EMULATOR_ADDRESS = "emulatorAddress";
const CONFIG_NUM_ACCOUNTS = "numAccounts";

// A created account that we can submit transactions for.
type Account = {
// An account that can be used to submit transactions.
export class Account {
index: number
address: string
};

type AccountSet = {[key: string]: Account};
constructor(index: number, address: string) {
this.index = index;
this.address = address;
}

name(): string {
return this.index === 0 ? "Service Account" : `Account ${this.index}`;
}

fullName(): string {
return `${this.name()} (${addAddressPrefix(this.address)})`;
}
};

// The subset of extension configuration used by the language server.
type ServerConfig = {
rootPrivateKey: string
rootKeySignatureAlgorithm: string
rootKeyHashAlgorithm: string
servicePrivateKey: string
serviceKeySignatureAlgorithm: string
serviceKeyHashAlgorithm: string
emulatorAddress: string
};

// The config used by the extension
// The configuration used by the extension.
export class Config {
// The name of the flow CLI executable
flowCommand: string;
serverConfig: ServerConfig;
numAccounts: number;
// Set of created accounts for which we can submit transactions.
// Mapping from account address to account object.
accounts: AccountSet;
// Address of the currently active account.
activeAccount: string;
accounts: Array<Account>;
// Index of the currently active account.
activeAccount: number;

constructor(flowCommand: string, numAccounts: number, serverConfig: ServerConfig) {
this.flowCommand = flowCommand;
this.numAccounts = numAccounts;
this.serverConfig = serverConfig;
this.accounts = {[ROOT_ADDR]: {address: ROOT_ADDR}};
this.activeAccount = ROOT_ADDR;
this.accounts = [new Account(0, SERVICE_ADDR)];
this.activeAccount = 0;
}

addAccount(address: string) {
address = shortAddress(address);
this.accounts[address] = {address: address};
const index = this.accounts.length;
this.accounts.push(new Account(index, address));
}

setActiveAccount(index: number) {
this.activeAccount = index;
}

getActiveAccount(): Account {
return this.accounts[this.activeAccount];
}

getAccount(index: number): Account|null {
if (index < 0 || index >= this.accounts.length) {
return null;
}

return this.accounts[index]
}

// Resets account state
resetAccounts() {
this.accounts = {[ROOT_ADDR]: {address: ROOT_ADDR}};
this.activeAccount = ROOT_ADDR;
this.accounts = [new Account(0, SERVICE_ADDR)];
this.activeAccount = 0;
}
}

Expand All @@ -67,19 +95,19 @@ export function getConfig(): Config {
throw new Error(`Missing ${CONFIG_FLOW_COMMAND} config`);
}

const rootPrivateKey: string | undefined = cadenceConfig.get(CONFIG_ROOT_PRIVATE_KEY);
if (!rootPrivateKey) {
throw new Error(`Missing ${CONFIG_ROOT_PRIVATE_KEY} config`);
const servicePrivateKey: string | undefined = cadenceConfig.get(CONFIG_SERVICE_PRIVATE_KEY);
if (!servicePrivateKey) {
throw new Error(`Missing ${CONFIG_SERVICE_PRIVATE_KEY} config`);
}

const rootKeySignatureAlgorithm: string | undefined = cadenceConfig.get(CONFIG_ROOT_KEY_SIGNATURE_ALGORITHM);
if (!rootKeySignatureAlgorithm) {
throw new Error(`Missing ${CONFIG_ROOT_KEY_SIGNATURE_ALGORITHM} config`);
const serviceKeySignatureAlgorithm: string | undefined = cadenceConfig.get(CONFIG_SERVICE_KEY_SIGNATURE_ALGORITHM);
if (!serviceKeySignatureAlgorithm) {
throw new Error(`Missing ${CONFIG_SERVICE_KEY_SIGNATURE_ALGORITHM} config`);
}

const rootKeyHashAlgorithm: string | undefined = cadenceConfig.get(CONFIG_ROOT_KEY_HASH_ALGORITHM);
if (!rootKeyHashAlgorithm) {
throw new Error(`Missing ${CONFIG_ROOT_KEY_HASH_ALGORITHM} config`);
const serviceKeyHashAlgorithm: string | undefined = cadenceConfig.get(CONFIG_SERVICE_KEY_HASH_ALGORITHM);
if (!serviceKeyHashAlgorithm) {
throw new Error(`Missing ${CONFIG_SERVICE_KEY_HASH_ALGORITHM} config`);
}

const emulatorAddress: string | undefined = cadenceConfig.get(CONFIG_EMULATOR_ADDRESS);
Expand All @@ -93,9 +121,9 @@ export function getConfig(): Config {
}

const serverConfig: ServerConfig = {
rootPrivateKey,
rootKeySignatureAlgorithm,
rootKeyHashAlgorithm,
servicePrivateKey,
serviceKeySignatureAlgorithm,
serviceKeyHashAlgorithm,
emulatorAddress
};

Expand Down
2 changes: 1 addition & 1 deletion tools/vscode-extension/src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,5 +53,5 @@ export function activate(ctx: ExtensionContext) {
export function deactivate() {}

export function renderExtension(ext: Extension) {
updateActiveAccountStatusBarItem(ext.activeAccountStatusBarItem, ext.config.activeAccount);
updateActiveAccountStatusBarItem(ext.activeAccountStatusBarItem, ext.config.getActiveAccount());
}
5 changes: 3 additions & 2 deletions tools/vscode-extension/src/status-bar.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import {
StatusBarItem,
StatusBarAlignment,
} from "vscode";
import {Account} from "./config";
import {SWITCH_ACCOUNT} from "./commands";

export function createActiveAccountStatusBarItem(): StatusBarItem {
Expand All @@ -11,7 +12,7 @@ export function createActiveAccountStatusBarItem(): StatusBarItem {
return statusBarItem
}

export function updateActiveAccountStatusBarItem(statusBarItem: StatusBarItem, activeAccount: string): void {
statusBarItem.text = `$(key) Active account: ${activeAccount}`
export function updateActiveAccountStatusBarItem(statusBarItem: StatusBarItem, activeAccount: Account): void {
statusBarItem.text = `$(key) Active account: ${activeAccount.fullName()}`
statusBarItem.show()
}