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

Enable all commands when config cannot be loaded #955

Merged
merged 8 commits into from Jul 8, 2022
8 changes: 6 additions & 2 deletions taqueria-plugin-taquito/originate.ts
@@ -1,4 +1,5 @@
import {
getCurrentEnvironment,
getCurrentEnvironmentConfig,
getDefaultAccount,
getInitialStorage,
Expand Down Expand Up @@ -61,7 +62,9 @@ const getValidContracts = async (parsedArgs: Opts) => {
(retval, filename) => {
const storage = getInitialStorage(parsedArgs)(filename);
if (storage === undefined || storage === null) {
sendErr(`No initial storage provided for ${filename}`);
sendErr(`Michelson artifact ${filename} has no initial storage specified. Storage is expected to be specified in .taq/config.json at JSON path: environment.${
ac10n marked this conversation as resolved.
Show resolved Hide resolved
getCurrentEnvironment(parsedArgs)
}.storage."${filename}"`);
return retval;
}
return [...retval, { filename, storage }];
Expand Down Expand Up @@ -181,8 +184,9 @@ const originateToSandboxes = (parsedArgs: Opts, currentEnv: Protocol.Environment
const first = getFirstAccountAlias(sandboxName, parsedArgs);
if (first) {
defaultAccount = getSandboxAccountConfig(parsedArgs)(sandboxName)(first);
// TODO: The error should be a warning, not an error. Descriptive string should not begin with 'Warning:'
sendErr(
`No default account has been configured for the sandbox called ${sandboxName}. Using the account called ${first} for origination.`,
`Warning: A default origination account is not specified for sandbox ${sandboxName}. Using the account ${first} for this origination. Specify a default account in .taq/config.json at JSON path: sandbox.${sandboxName}.accounts.default`,
ac10n marked this conversation as resolved.
Show resolved Hide resolved
);
}
}
Expand Down
60 changes: 6 additions & 54 deletions taqueria-vscode-extension/package.json
Expand Up @@ -86,28 +86,28 @@
"category": "Taqueria",
"title": "Initialize Project",
"shortTitle": "init",
"enablement": "!@taqueria-state/is-taqified"
"enablement": "@taqueria-state/enable-init-scaffold"
},
{
"command": "taqueria.scaffold",
"category": "Taqueria",
"title": "Scaffold Project",
"shortTitle": "scaffold",
"enablement": "!@taqueria-state/is-taqified"
"enablement": "@taqueria-state/enable-init-scaffold"
},
{
"command": "taqueria.install",
"category": "Taqueria",
"title": "Install Plugin",
"shortTitle": "install",
"enablement": "@taqueria-state/is-taqified && @taqueria-state/not-installed-plugin-count != 0"
"enablement": "@taqueria-state/enable-install-uninstall && @taqueria-state/not-installed-plugin-count != 0"
},
{
"command": "taqueria.uninstall",
"category": "Taqueria",
"title": "Uninstall Plugin",
"shortTitle": "uninstall",
"enablement": "@taqueria-state/is-taqified && @taqueria-state/installed-plugin-count != 0"
"enablement": "@taqueria-state/enable-install-uninstall && @taqueria-state/installed-plugin-count != 0"
},
{
"command": "taqueria.compile_smartpy",
Expand Down Expand Up @@ -182,7 +182,7 @@
"category": "Taqueria",
"title": "Typecheck Michelson contracts",
"shortTitle": "typecheck",
"enablement": "@taqueria/plugin-contract-types"
"enablement": "@taqueria/plugin-tezos-client"
},
{
"command": "taqueria.test",
Expand All @@ -191,55 +191,7 @@
"shortTitle": "test",
"enablement": "@taqueria/plugin-jest"
}
],
"menus": {
"commandPalette": [
{
"command": "taqueria.init",
"when": "!@taqueria-state/is-taqified"
},
{
"command": "taqueria.install",
"when": "@taqueria-state/is-taqified"
},
{
"command": "taqueria.compile_ligo",
"when": "@taqueria/plugin-ligo"
},
{
"command": "taqueria.compile_smartpy",
"when": "@taqueria/plugin-smartpy"
},
{
"command": "taqueria.start_sandbox",
"when": "@taqueria/plugin-flextesa"
},
{
"command": "taqueria.stop_sandbox",
"when": "@taqueria/plugin-flextesa"
},
{
"command": "taqueria.list_accounts",
"when": "@taqueria/plugin-flextesa"
},
{
"command": "taqueria.originate",
"when": "@taqueria/plugin-taquito"
},
{
"command": "taqueria.generate_types",
"when": "@taqueria/plugin-contract-types"
},
{
"command": "taqueria.typecheck",
"when": "@taqueria/plugin-contract-types"
},
{
"command": "taqueria.test",
"enablement": "@taqueria/plugin-jest"
}
]
}
]
},
"scripts": {
"vscode:prepublish": "npm run compile",
Expand Down
10 changes: 6 additions & 4 deletions taqueria-vscode-extension/src/extension.ts
Expand Up @@ -7,19 +7,21 @@ import { makeDir } from './lib/pure';

const { clearConfigWatchers, getConfigWatchers, addConfigWatcherIfNotExists } = (() => {
const inMemoryState = {
configWatchers: new Map<string, api.FileSystemWatcher>(),
configWatchers: new Map<string, api.FileSystemWatcher[]>(),
};

const clearConfigWatchers = () => {
for (const watcher of inMemoryState.configWatchers.values()) {
watcher.dispose();
for (const watcherList of inMemoryState.configWatchers.values()) {
for (const watcher of watcherList) {
watcher.dispose();
}
}
inMemoryState.configWatchers.clear();
};

const getConfigWatchers = () => inMemoryState.configWatchers.values();

const addConfigWatcherIfNotExists = (folder: string, factory: () => api.FileSystemWatcher) => {
const addConfigWatcherIfNotExists = (folder: string, factory: () => api.FileSystemWatcher[]) => {
if (inMemoryState.configWatchers.has(folder)) {
return;
}
Expand Down
126 changes: 87 additions & 39 deletions taqueria-vscode-extension/src/lib/helpers.ts
Expand Up @@ -603,39 +603,79 @@ export const inject = (deps: InjectedDependencies) => {
projectDir: Util.PathToDir,
) => {
showOutput(output)(OutputLevels.debug, 'Project config changed, updating command states...');
let taqFolderFound: boolean;
try {
const config = await Util.TaqifiedDir.create(projectDir, i18n);
const availablePlugins = await getAvailablePlugins(context);
const availablePluginsNotInstalled = config.config?.plugins
? availablePlugins.filter(name => config.config.plugins?.findIndex(p => p.name === name) === -1)
: availablePlugins;
showOutput(output)(OutputLevels.debug, `@taqueria-state/is-taqified: ${!!config.config}`);
vscode.commands.executeCommand('setContext', '@taqueria-state/is-taqified', !!config.config);
vscode.commands.executeCommand(
'setContext',
'@taqueria-state/installed-plugin-count',
config.config?.plugins?.length ?? 0,
);
vscode.commands.executeCommand(
'setContext',
'@taqueria-state/not-installed-plugin-count',
availablePluginsNotInstalled.length,
);
const plugins = getWellKnownPlugins();
showOutput(output)(OutputLevels.debug, `Known plugins: ${JSON.stringify(plugins)}`);
for (const plugin of plugins) {
const found = config.config?.plugins?.find(item => item.name === plugin) !== undefined;
showOutput(output)(OutputLevels.debug, `plugins ${plugin}: ${found}`);
vscode.commands.executeCommand('setContext', plugin, found);
}
} catch (e: any) {
if (
e.code === 'E_NOT_TAQIFIED' && e.msg === `The given directory is not taqified as it's missing a .taq directory.`
) {
return;
await Util.makeDir(join(projectDir, '.taq'), i18n);
showOutput(output)(OutputLevels.debug, 'Taq folder is found');
taqFolderFound = true;
} catch {
taqFolderFound = false;
showOutput(output)(OutputLevels.debug, 'Taq folder not found');
}
let enableAllCommands: boolean;
let config: Util.TaqifiedDir | null;
try {
config = await Util.TaqifiedDir.create(projectDir, i18n);
enableAllCommands = false;
} catch (e: unknown) {
config = null;
enableAllCommands = taqFolderFound;
// We don't want to show messages to users when they are working with non-taqified folders (Except when output level is set to info or more verbose)
if (shouldOutput(OutputLevels.info, output.logLevel) || taqFolderFound) {
vscode.commands.executeCommand('setContext', '@taqueria-state/enable-all-commands', true);
showOutput(output)(OutputLevels.error, 'Error: Could not update command states:');
logAllNestedErrors(e, output);
if (taqFolderFound) {
showOutput(output)(
OutputLevels.warn,
'The Taqueria config could not be loaded, all commands will be enabled.\nPlease try fixing the errors in config.json',
ac10n marked this conversation as resolved.
Show resolved Hide resolved
);
}
}
showOutput(output)(OutputLevels.error, 'Error: Could not update command states:');
logAllNestedErrors(e, output);
}
const availablePlugins = await getAvailablePlugins(context);
const availablePluginsNotInstalled = config?.config?.plugins
? availablePlugins.filter(name => config?.config.plugins?.findIndex(p => p.name === name) === -1)
: availablePlugins;
showOutput(output)(
OutputLevels.debug,
`@taqueria-state/enable-init-scaffold: ${enableAllCommands || !config?.config}`,
);
vscode.commands.executeCommand(
'setContext',
'@taqueria-state/enable-init-scaffold',
enableAllCommands || !config?.config,
);

showOutput(output)(
OutputLevels.debug,
`@taqueria-state/enable-install-uninstall: ${enableAllCommands || !!config?.config}`,
);
vscode.commands.executeCommand(
'setContext',
'@taqueria-state/enable-install-uninstall',
enableAllCommands || !!config?.config,
);

showOutput(output)(OutputLevels.debug, `@taqueria-state/is-taqified: ${!!config?.config}`);
vscode.commands.executeCommand('setContext', '@taqueria-state/is-taqified', !!config?.config);

vscode.commands.executeCommand(
'setContext',
'@taqueria-state/installed-plugin-count',
enableAllCommands ? 1 : config?.config?.plugins?.length ?? 0,
);
vscode.commands.executeCommand(
'setContext',
'@taqueria-state/not-installed-plugin-count',
enableAllCommands ? 1 : availablePluginsNotInstalled.length,
);
const plugins = getWellKnownPlugins();
showOutput(output)(OutputLevels.debug, `Known plugins: ${JSON.stringify(plugins)}`);
for (const plugin of plugins) {
const found = config?.config?.plugins?.find(item => item.name === plugin) !== undefined;
showOutput(output)(OutputLevels.debug, `plugins ${plugin}: ${found}`);
vscode.commands.executeCommand('setContext', plugin, enableAllCommands || found);
}
};

Expand All @@ -644,7 +684,7 @@ export const inject = (deps: InjectedDependencies) => {
output: Output,
i18n: i18n,
projectDir: Util.PathToDir,
addConfigWatcherIfNotExists: (folder: string, factory: () => api.FileSystemWatcher) => void,
addConfigWatcherIfNotExists: (folder: string, factory: () => api.FileSystemWatcher[]) => void,
) => {
showOutput(output)(OutputLevels.debug, `Directory ${projectDir} should be watched.`);
addConfigWatcherIfNotExists(projectDir, () => {
Expand All @@ -655,15 +695,23 @@ export const inject = (deps: InjectedDependencies) => {
logAllNestedErrors(error, output);
}
try {
// TODO: this does not trigger when .taq folder is deleted.
const watcher = vscode.workspace.createFileSystemWatcher(join(projectDir, '.taq/config.json'));
// TODO: We should detect the event that VsCode's current Folder is changed and the watcher should be disposed
const folderWatcher = vscode.workspace.createFileSystemWatcher(join(projectDir, '.taq'));
const configWatcher = vscode.workspace.createFileSystemWatcher(join(projectDir, '.taq/config.json'));
const stateWatcher = vscode.workspace.createFileSystemWatcher(join(projectDir, '.taq/state.json'));

// TODO: Is passing these arguments to the callback of a long lived watcher prevent GC? Are these short lived objects?
watcher.onDidChange((e: api.Uri) => updateCommandStates(context, output, i18n, projectDir));
watcher.onDidCreate((e: api.Uri) => updateCommandStates(context, output, i18n, projectDir));
watcher.onDidDelete((e: api.Uri) => updateCommandStates(context, output, i18n, projectDir));
return watcher;
folderWatcher.onDidChange((e: api.Uri) => updateCommandStates(context, output, i18n, projectDir));
folderWatcher.onDidCreate((e: api.Uri) => updateCommandStates(context, output, i18n, projectDir));
folderWatcher.onDidDelete((e: api.Uri) => updateCommandStates(context, output, i18n, projectDir));

configWatcher.onDidChange((e: api.Uri) => updateCommandStates(context, output, i18n, projectDir));
configWatcher.onDidCreate((e: api.Uri) => updateCommandStates(context, output, i18n, projectDir));
configWatcher.onDidDelete((e: api.Uri) => updateCommandStates(context, output, i18n, projectDir));

stateWatcher.onDidChange((e: api.Uri) => updateCommandStates(context, output, i18n, projectDir));
stateWatcher.onDidCreate((e: api.Uri) => updateCommandStates(context, output, i18n, projectDir));
stateWatcher.onDidDelete((e: api.Uri) => updateCommandStates(context, output, i18n, projectDir));
return [folderWatcher, configWatcher, stateWatcher];
} catch (error: unknown) {
throw {
kind: 'E_UnknownError',
Expand Down