First version of cancelable and reportable commands

This commit is contained in:
Nick Chen 2017-06-26 19:24:48 -07:00
Родитель de6d2810b2
Коммит 470ddc98ee
23 изменённых файлов: 500 добавлений и 221 удалений

2
.gitignore поставляемый
Просмотреть файл

@ -67,4 +67,4 @@ jspm_packages/
*.vsix
# VS Code tests
.vscode-test/**
.vscode-test/

6
.vscode/launch.json поставляемый
Просмотреть файл

@ -26,7 +26,7 @@
"outFiles": [
"${workspaceRoot}/packages/*/out/src/**/*.js"
],
"preLaunchTask": "Watch"
"preLaunchTask": "Compile"
},
{
"name": "Launch SalesforceDX VS Code Core Tests",
@ -42,7 +42,7 @@
"outFiles": [
"${workspaceRoot}/packages/*/out/**/*.js"
],
"preLaunchTask": "Watch"
"preLaunchTask": "Compile"
},
{
"name": "Launch SalesforceDX VS Code Apex Tests",
@ -58,7 +58,7 @@
"outFiles": [
"${workspaceRoot}/packages/*/out/**/*.js"
],
"preLaunchTask": "Watch"
"preLaunchTask": "Compile"
}
]
}

12
.vscode/tasks.json поставляемый
Просмотреть файл

@ -31,6 +31,18 @@
],
"isBackground": true,
"problemMatcher": "$tsc-watch"
},
{
"taskName": "Compile",
"command": "lerna",
"isShellCommand": true,
"showOutput": "silent",
"args": [
"run",
"compile"
],
"isBackground": false,
"problemMatcher": "$tsc-watch"
}
]
}

Просмотреть файл

@ -32,8 +32,6 @@ You would usually do the following each time you close/reopen VS Code:
1. Open the Command Palette > Tasks: Run Task > Bootstrap (this essentially
runs `lerna bootstrap`).
1. Open the Command Palette > Tasks: Run Task > Watch (this essentially runs
`lerna run --parallel watch`).
1. In VS Code, open the debug view (Ctrl+Shift+D or Cmd+Shift+D on Mac) and from
the launch configuration dropdown, pick "Launch Extensions".
1. In VS Code, open the debug view (Ctrl+Shift+D or Cmd+Shift+D on Mac) and from

Просмотреть файл

@ -9,7 +9,8 @@
"Other"
],
"dependencies": {
"rxjs": "^5.4.1"
"rxjs": "^5.4.1",
"tree-kill": "^1.1.0"
},
"devDependencies": {
"@types/chai": "^4.0.0",
@ -29,4 +30,4 @@
"coverage": "./node_modules/.bin/nyc npm test"
},
"main": "./out/src/"
}
}

Просмотреть файл

@ -8,6 +8,12 @@ export class Command {
this.description = builder.description;
this.args = builder.args;
}
public toString(): string {
return this.description
? this.description
: `${this.command} ${this.args.join(' ')}`;
}
}
export class CommandBuilder {

Просмотреть файл

@ -1,10 +1,13 @@
import { Command } from './';
import { spawn, ChildProcess, ExecOptions } from 'child_process';
import { Observable } from 'rxjs/Observable';
import { Subscription } from 'rxjs/Subscription';
import 'rxjs/add/observable/fromEvent';
import 'rxjs/add/observable/interval';
const kill = require('tree-kill');
import { Command } from './';
export interface CancellationToken {
isCancellationRequested: boolean;
}
@ -24,19 +27,25 @@ export class CliCommandExecutor {
this.command.args,
this.options
);
return new CommandExecution(childProcess, cancellationToken);
return new CommandExecution(this.command, childProcess, cancellationToken);
}
}
export class CommandExecution {
public readonly command: Command;
public readonly cancellationToken?: CancellationToken;
public readonly processExitSubject: Observable<number | string>;
public readonly stdoutSubject: Observable<Buffer | string>;
public readonly stderrSubject: Observable<Buffer | string>;
constructor(
command: Command,
childProcess: ChildProcess,
cancellationToken?: CancellationToken
) {
this.command = command;
this.cancellationToken = cancellationToken;
let timerSubscriber: Subscription | null;
// Process
@ -54,15 +63,26 @@ export class CommandExecution {
// Cancellation watcher
if (cancellationToken) {
const timer = Observable.interval(1000);
timerSubscriber = timer.subscribe(next => {
timerSubscriber = timer.subscribe(async next => {
if (cancellationToken.isCancellationRequested) {
try {
childProcess.kill();
await killPromise(childProcess.pid);
} catch (e) {
// This is best effort, by the time we get here, the process might have been killed.
console.log(e);
}
}
});
}
}
}
// This is required because of https://github.com/nodejs/node/issues/6052
// Basically if a child process spawns it own children processes, those
// children (grandchildren) processes are not necessarily killed
async function killPromise(processId: number) {
return new Promise((resolve, reject) => {
kill(processId, 'SIGKILL', (err: any) => {
err ? reject(err) : resolve();
});
});
}

Просмотреть файл

@ -13,7 +13,8 @@
"Other"
],
"dependencies": {
"@salesforce/salesforcedx-utils-vscode": "0.1.0"
"@salesforce/salesforcedx-utils-vscode": "0.1.0",
"vscode-nls": "^2.0.2"
},
"devDependencies": {
"@types/chai": "^4.0.0",
@ -79,4 +80,4 @@
}
]
}
}
}

Просмотреть файл

@ -0,0 +1,27 @@
import * as vscode from 'vscode';
import { CommandExecution } from '@salesforce/salesforcedx-utils-vscode/out/src/cli';
export const DEFAULT_SFDX_CHANNEL = vscode.window.createOutputChannel(
'SalesforceDX - CLI'
);
export function streamCommandOutput(execution: CommandExecution) {
DEFAULT_SFDX_CHANNEL.append('Starting ');
DEFAULT_SFDX_CHANNEL.appendLine(execution.command.toString());
DEFAULT_SFDX_CHANNEL.appendLine('');
execution.stderrSubject.subscribe(data =>
DEFAULT_SFDX_CHANNEL.append(data.toString())
);
execution.stdoutSubject.subscribe(data =>
DEFAULT_SFDX_CHANNEL.append(data.toString())
);
execution.processExitSubject.subscribe(data => {
DEFAULT_SFDX_CHANNEL.append(execution.command.toString());
DEFAULT_SFDX_CHANNEL.appendLine(
'ended' + data == null ? ` with exit code ${data.toString()}` : ''
);
});
}

Просмотреть файл

@ -1,196 +0,0 @@
import * as vscode from 'vscode';
import * as path from 'path';
import * as status from './status';
import {
CliCommandExecutor,
SfdxCommandBuilder
} from '@salesforce/salesforcedx-utils-vscode/out/src/cli';
const channel = vscode.window.createOutputChannel('SalesforceDX - CLI');
export function forceAuthWebLogin() {
channel.appendLine('force:auth:web:login --setdefaultdevhubusername');
status.showStatus('Authenticating');
const execution = new CliCommandExecutor(
new SfdxCommandBuilder()
.withArg('force:auth:web:login')
.withArg('--setdefaultdevhubusername')
.build(),
{ cwd: vscode.workspace.rootPath }
).execute();
execution.stderrSubject.subscribe(data => channel.append(data.toString()));
execution.stdoutSubject.subscribe(data => channel.append(data.toString()));
execution.processExitSubject.subscribe(data => status.hideStatus());
}
export function forceOrgCreate() {
channel.appendLine('force:org:create');
vscode.workspace.findFiles('config/*.json', '').then(files => {
const fileItems: vscode.QuickPickItem[] = files.map(file => {
return {
label: path.basename(file.toString()),
description: file.fsPath
};
});
vscode.window.showQuickPick(fileItems).then(selection => {
if (selection) {
status.showStatus('Creating org');
const rootPath = vscode.workspace.rootPath!;
const selectionPath = path.relative(
rootPath,
selection.description.toString()
);
const execution = new CliCommandExecutor(
new SfdxCommandBuilder()
.withArg('force:org:create')
.withFlag('-f', `${selectionPath}`)
.withArg('--setdefaultusername')
.build(),
{ cwd: vscode.workspace.rootPath }
).execute();
execution.stderrSubject.subscribe(data =>
channel.append(data.toString())
);
execution.stdoutSubject.subscribe(data =>
channel.append(data.toString())
);
execution.processExitSubject.subscribe(data => status.hideStatus());
}
});
});
}
export function forceOrgOpen() {
channel.appendLine('force:org:open');
status.showStatus('Opening org');
const execution = new CliCommandExecutor(
new SfdxCommandBuilder().withArg('force:org:open').build(),
{ cwd: vscode.workspace.rootPath }
).execute();
execution.stderrSubject.subscribe(data => channel.append(data.toString()));
execution.stdoutSubject.subscribe(data => channel.append(data.toString()));
execution.processExitSubject.subscribe(data => status.hideStatus());
}
export function forceSourcePull() {
channel.appendLine('force:source:pull');
status.showStatus('Pulling from org...');
const execution = new CliCommandExecutor(
new SfdxCommandBuilder().withArg('force:source:pull').build(),
{ cwd: vscode.workspace.rootPath }
).execute();
execution.stderrSubject.subscribe(data => channel.append(data.toString()));
execution.stdoutSubject.subscribe(data => channel.append(data.toString()));
execution.processExitSubject.subscribe(data => status.hideStatus());
}
export function forceSourcePush() {
channel.appendLine('force:source:push');
status.showStatus('Pushing to org');
const execution = new CliCommandExecutor(
new SfdxCommandBuilder().withArg('force:source:push').build(),
{ cwd: vscode.workspace.rootPath }
).execute();
execution.stderrSubject.subscribe(data => channel.append(data.toString()));
execution.stdoutSubject.subscribe(data => channel.append(data.toString()));
execution.processExitSubject.subscribe(data => status.hideStatus());
}
export function forceSourceStatus() {
channel.appendLine('force:source:status');
status.showStatus('Checking status against org');
const execution = new CliCommandExecutor(
new SfdxCommandBuilder().withArg('force:source:status').build(),
{ cwd: vscode.workspace.rootPath }
).execute();
execution.stderrSubject.subscribe(data => channel.append(data.toString()));
execution.stdoutSubject.subscribe(data => channel.append(data.toString()));
execution.processExitSubject.subscribe(data => status.hideStatus());
}
export function forceApexTestRun(testClass?: string) {
channel.appendLine('force:apex:test:run');
if (testClass) {
status.showStatus('Running apex tests');
const execution = new CliCommandExecutor(
new SfdxCommandBuilder()
.withArg('force:apex:test:run')
.withFlag('--classnames', `${testClass}`)
.withFlag('--resultformat', 'human')
.build(),
{ cwd: vscode.workspace.rootPath }
).execute();
execution.stderrSubject.subscribe(data => channel.append(data.toString()));
execution.stdoutSubject.subscribe(data => channel.append(data.toString()));
execution.processExitSubject.subscribe(data => status.hideStatus());
} else {
vscode.workspace.findFiles('**/*.testSuite-meta.xml', '').then(files => {
const fileItems: vscode.QuickPickItem[] = files.map(file => {
return {
label: path
.basename(file.toString())
.replace('.testSuite-meta.xml', ''),
description: file.fsPath
};
});
fileItems.push({
label: 'All tests',
description: 'Runs all tests in the current workspace'
});
vscode.window.showQuickPick(fileItems).then(selection => {
if (selection) {
status.showStatus('Running apex tests');
if (selection.label === 'All tests') {
const execution = new CliCommandExecutor(
new SfdxCommandBuilder()
.withArg('force:apex:test:run')
.withFlag('--resultformat', 'human')
.build(),
{ cwd: vscode.workspace.rootPath }
).execute();
execution.stderrSubject.subscribe(data =>
channel.append(data.toString())
);
execution.stdoutSubject.subscribe(data =>
channel.append(data.toString())
);
execution.processExitSubject.subscribe(data => status.hideStatus());
} else {
const execution = new CliCommandExecutor(
new SfdxCommandBuilder()
.withArg('force:apex:test:run')
.withFlag('--suitenames', `${selection.label}`)
.withFlag('--resultformat', 'human')
.build(),
{ cwd: vscode.workspace.rootPath }
).execute();
execution.stderrSubject.subscribe(data =>
channel.append(data.toString())
);
execution.stdoutSubject.subscribe(data =>
channel.append(data.toString())
);
execution.processExitSubject.subscribe(data => status.hideStatus());
}
}
});
});
}
}

Просмотреть файл

@ -0,0 +1,91 @@
import * as path from 'path';
import * as vscode from 'vscode';
import {
CliCommandExecutor,
SfdxCommandBuilder
} from '@salesforce/salesforcedx-utils-vscode/out/src/cli';
import { streamCommandOutput } from '../channels';
import { reportExecutionStatus } from '../notifications';
import { CancellableStatusBar } from '../statuses';
export function forceApexTestRun(testClass?: string) {
if (testClass) {
runTestClass(testClass);
} else {
vscode.workspace.findFiles('**/*.testSuite-meta.xml', '').then(files => {
const fileItems: vscode.QuickPickItem[] = files.map(file => {
return {
label: path
.basename(file.toString())
.replace('.testSuite-meta.xml', ''),
description: file.fsPath
};
});
fileItems.push({
label: 'All tests',
description: 'Runs all tests in the current workspace'
});
vscode.window.showQuickPick(fileItems).then(selection => {
if (selection) {
if (selection.label === 'All tests') {
runAllTests();
} else {
runTestSuite(selection.label);
}
}
});
});
}
}
function runTestClass(testClass: string) {
const cancellationTokenSource = new vscode.CancellationTokenSource();
const cancellationToken = cancellationTokenSource.token;
const execution = new CliCommandExecutor(
new SfdxCommandBuilder()
.withArg('force:apex:test:run')
.withFlag('--classnames', `${testClass}`)
.withFlag('--resultformat', 'human')
.build(),
{ cwd: vscode.workspace.rootPath }
).execute(cancellationToken);
streamCommandOutput(execution);
reportExecutionStatus(execution, cancellationToken);
CancellableStatusBar.show(execution, cancellationTokenSource);
}
function runAllTests() {
const cancellationTokenSource = new vscode.CancellationTokenSource();
const cancellationToken = cancellationTokenSource.token;
const execution = new CliCommandExecutor(
new SfdxCommandBuilder()
.withArg('force:apex:test:run')
.withFlag('--resultformat', 'human')
.build(),
{ cwd: vscode.workspace.rootPath }
).execute(cancellationToken);
streamCommandOutput(execution);
reportExecutionStatus(execution, cancellationToken);
CancellableStatusBar.show(execution, cancellationTokenSource);
}
function runTestSuite(testSuiteName: string) {
const cancellationTokenSource = new vscode.CancellationTokenSource();
const cancellationToken = cancellationTokenSource.token;
const execution = new CliCommandExecutor(
new SfdxCommandBuilder()
.withArg('force:apex:test:run')
.withFlag('--suitenames', `${testSuiteName}`)
.withFlag('--resultformat', 'human')
.build(),
{ cwd: vscode.workspace.rootPath }
).execute(cancellationToken);
streamCommandOutput(execution);
reportExecutionStatus(execution, cancellationToken);
CancellableStatusBar.show(execution, cancellationTokenSource);
}

Просмотреть файл

@ -0,0 +1,25 @@
import * as vscode from 'vscode';
import {
CliCommandExecutor,
SfdxCommandBuilder
} from '@salesforce/salesforcedx-utils-vscode/out/src/cli';
import { streamCommandOutput } from '../channels';
import { reportExecutionStatus } from '../notifications';
import { CancellableStatusBar } from '../statuses';
export function forceAuthWebLogin() {
const cancellationTokenSource = new vscode.CancellationTokenSource();
const cancellationToken = cancellationTokenSource.token;
const execution = new CliCommandExecutor(
new SfdxCommandBuilder()
.withArg('force:auth:web:login')
.withArg('--setdefaultdevhubusername')
.build(),
{ cwd: vscode.workspace.rootPath }
).execute(cancellationTokenSource.token);
streamCommandOutput(execution);
reportExecutionStatus(execution, cancellationToken);
CancellableStatusBar.show(execution, cancellationTokenSource);
}

Просмотреть файл

@ -0,0 +1,44 @@
import * as vscode from 'vscode';
import * as path from 'path';
import {
CliCommandExecutor,
SfdxCommandBuilder
} from '@salesforce/salesforcedx-utils-vscode/out/src/cli';
import { streamCommandOutput } from '../channels';
import { reportExecutionStatus } from '../notifications';
import { CancellableStatusBar } from '../statuses';
export function forceOrgCreate() {
vscode.workspace.findFiles('config/*.json', '').then(files => {
const fileItems: vscode.QuickPickItem[] = files.map(file => {
return {
label: path.basename(file.toString()),
description: file.fsPath
};
});
vscode.window.showQuickPick(fileItems).then(selection => {
if (selection) {
const cancellationTokenSource = new vscode.CancellationTokenSource();
const cancellationToken = cancellationTokenSource.token;
const rootPath = vscode.workspace.rootPath!;
const selectionPath = path.relative(
rootPath,
selection.description.toString()
);
const execution = new CliCommandExecutor(
new SfdxCommandBuilder()
.withArg('force:org:create')
.withFlag('-f', `${selectionPath}`)
.withArg('--setdefaultusername')
.build(),
{ cwd: rootPath }
).execute(cancellationToken);
streamCommandOutput(execution);
reportExecutionStatus(execution, cancellationToken);
CancellableStatusBar.show(execution, cancellationTokenSource);
}
});
});
}

Просмотреть файл

@ -0,0 +1,22 @@
import * as vscode from 'vscode';
import {
CliCommandExecutor,
SfdxCommandBuilder
} from '@salesforce/salesforcedx-utils-vscode/out/src/cli';
import { streamCommandOutput } from '../channels';
import { reportExecutionStatus } from '../notifications';
import { CancellableStatusBar } from '../statuses';
export function forceOrgOpen() {
const cancellationTokenSource = new vscode.CancellationTokenSource();
const cancellationToken = cancellationTokenSource.token;
const execution = new CliCommandExecutor(
new SfdxCommandBuilder().withArg('force:org:open').build(),
{ cwd: vscode.workspace.rootPath }
).execute(cancellationToken);
streamCommandOutput(execution);
reportExecutionStatus(execution, cancellationToken);
CancellableStatusBar.show(execution, cancellationTokenSource);
}

Просмотреть файл

@ -0,0 +1,22 @@
import * as vscode from 'vscode';
import {
CliCommandExecutor,
SfdxCommandBuilder
} from '@salesforce/salesforcedx-utils-vscode/out/src/cli';
import { streamCommandOutput } from '../channels';
import { reportExecutionStatus } from '../notifications';
import { CancellableStatusBar } from '../statuses';
export function forceSourcePull() {
const cancellationTokenSource = new vscode.CancellationTokenSource();
const cancellationToken = cancellationTokenSource.token;
const execution = new CliCommandExecutor(
new SfdxCommandBuilder().withArg('force:source:pull').build(),
{ cwd: vscode.workspace.rootPath }
).execute(cancellationToken);
streamCommandOutput(execution);
reportExecutionStatus(execution, cancellationToken);
CancellableStatusBar.show(execution, cancellationTokenSource);
}

Просмотреть файл

@ -0,0 +1,22 @@
import * as vscode from 'vscode';
import {
CliCommandExecutor,
SfdxCommandBuilder
} from '@salesforce/salesforcedx-utils-vscode/out/src/cli';
import { streamCommandOutput } from '../channels';
import { reportExecutionStatus } from '../notifications';
import { CancellableStatusBar } from '../statuses';
export function forceSourcePush() {
const cancellationTokenSource = new vscode.CancellationTokenSource();
const cancellationToken = cancellationTokenSource.token;
const execution = new CliCommandExecutor(
new SfdxCommandBuilder().withArg('force:source:push').build(),
{ cwd: vscode.workspace.rootPath }
).execute(cancellationToken);
streamCommandOutput(execution);
reportExecutionStatus(execution, cancellationToken);
CancellableStatusBar.show(execution, cancellationTokenSource);
}

Просмотреть файл

@ -0,0 +1,22 @@
import * as vscode from 'vscode';
import {
CliCommandExecutor,
SfdxCommandBuilder
} from '@salesforce/salesforcedx-utils-vscode/out/src/cli';
import { streamCommandOutput } from '../channels';
import { reportExecutionStatus } from '../notifications';
import { CancellableStatusBar } from '../statuses';
export function forceSourceStatus() {
const cancellationTokenSource = new vscode.CancellationTokenSource();
const cancellationToken = cancellationTokenSource.token;
const execution = new CliCommandExecutor(
new SfdxCommandBuilder().withArg('force:source:status').build(),
{ cwd: vscode.workspace.rootPath }
).execute(cancellationToken);
streamCommandOutput(execution);
reportExecutionStatus(execution, cancellationToken);
CancellableStatusBar.show(execution, cancellationTokenSource);
}

Просмотреть файл

@ -0,0 +1,7 @@
export { forceAuthWebLogin } from './forceAuthWebLogin';
export { forceApexTestRun } from './forceApexTestRun';
export { forceOrgCreate } from './forceOrgCreate';
export { forceOrgOpen } from './forceOrgOpen';
export { forceSourcePull } from './forceSourcePull';
export { forceSourcePush } from './forceSourcePush';
export { forceSourceStatus } from './forceSourceStatus';

Просмотреть файл

@ -1,37 +1,54 @@
import * as vscode from 'vscode';
import * as scratchOrgDecorator from './scratch-org-decorator';
import * as commands from './commands';
import { CANCEL_EXECUTION_COMMAND, cancelCommandExecution } from './statuses';
import {
forceApexTestRun,
forceAuthWebLogin,
forceOrgCreate,
forceOrgOpen,
forceSourcePull,
forceSourcePush,
forceSourceStatus
} from './commands';
function registerCommands(): vscode.Disposable {
// Customer-facing commands
const forceAuthWebLoginCmd = vscode.commands.registerCommand(
'sfdx.force.auth.web.login',
commands.forceAuthWebLogin
forceAuthWebLogin
);
const forceOrgCreateCmd = vscode.commands.registerCommand(
'sfdx.force.org.create',
commands.forceOrgCreate
forceOrgCreate
);
const forceOrgOpenCmd = vscode.commands.registerCommand(
'sfdx.force.org.open',
commands.forceOrgOpen
forceOrgOpen
);
const forceSourcePullCmd = vscode.commands.registerCommand(
'sfdx.force.source.pull',
commands.forceSourcePull
forceSourcePull
);
const forceSourcePushCmd = vscode.commands.registerCommand(
'sfdx.force.source.push',
commands.forceSourcePush
forceSourcePush
);
const forceSourceStatusCmd = vscode.commands.registerCommand(
'sfdx.force.source.status',
commands.forceSourceStatus
forceSourceStatus
);
const forceApexTestRunCmd = vscode.commands.registerCommand(
'sfdx.force.apex.test.run',
commands.forceApexTestRun
forceApexTestRun
);
// Internal commands
const internalCancelCommandExecution = vscode.commands.registerCommand(
CANCEL_EXECUTION_COMMAND,
cancelCommandExecution
);
return vscode.Disposable.from(
forceAuthWebLoginCmd,
forceOrgCreateCmd,
@ -39,7 +56,8 @@ function registerCommands(): vscode.Disposable {
forceSourcePullCmd,
forceSourcePushCmd,
forceSourceStatusCmd,
forceApexTestRunCmd
forceApexTestRunCmd,
internalCancelCommandExecution
);
}

Просмотреть файл

@ -0,0 +1,30 @@
import * as vscode from 'vscode';
import { CommandExecution } from '@salesforce/salesforcedx-utils-vscode/out/src/cli';
import { DEFAULT_SFDX_CHANNEL } from '../channels';
export function reportExecutionStatus(
execution: CommandExecution,
cancellationToken?: vscode.CancellationToken
) {
execution.processExitSubject.subscribe(async data => {
if (data !== null && data !== 'undefined' && data.toString() === '0') {
const selection = await vscode.window.showInformationMessage(
`Successfully executed ${execution.command}`,
'Show'
);
if (selection && selection === 'Show') {
DEFAULT_SFDX_CHANNEL.show();
}
} else {
if (cancellationToken && cancellationToken.isCancellationRequested) {
vscode.window.showWarningMessage(`${execution.command} canceled`);
DEFAULT_SFDX_CHANNEL.show();
} else {
vscode.window.showErrorMessage(
`Failed to execute ${execution.command}`
);
DEFAULT_SFDX_CHANNEL.show();
}
}
});
}

Просмотреть файл

@ -7,7 +7,7 @@ export function showStatus(status: string) {
if (!statusBarItem) {
statusBarItem = window.createStatusBarItem(StatusBarAlignment.Left, -10);
}
statusBarItem.text = `$(beaker) ${status}`;
statusBarItem.text = `$(clock) ${status}`;
statusTimer = setInterval(
() => (statusBarItem.text = statusBarItem.text + '.'),
1000

Просмотреть файл

@ -0,0 +1,6 @@
export {
CANCEL_EXECUTION_COMMAND,
PassiveStatusBar,
CancellableStatusBar,
cancelCommandExecution
} from './status';

Просмотреть файл

@ -0,0 +1,101 @@
import {
CancellationTokenSource,
StatusBarItem,
StatusBarAlignment,
window
} from 'vscode';
import { CommandExecution } from '@salesforce/salesforcedx-utils-vscode/out/src/cli';
export const CANCEL_EXECUTION_COMMAND = 'internal.cancel.execution.command';
const ALIGNMENT = StatusBarAlignment.Left;
const PRIORITY = -10;
const statusBarItem: StatusBarItem = window.createStatusBarItem(
ALIGNMENT,
PRIORITY
);
let statusTimer: NodeJS.Timer | null;
let cancellationTokenSource: CancellationTokenSource | null;
export class PassiveStatusBar {
public static show(execution: CommandExecution) {
return new PassiveStatusBar(execution);
}
private constructor(execution: CommandExecution) {
resetStatusBarItem();
statusBarItem.text = `$(clock) ${execution.command}`;
if (statusTimer) {
clearInterval(statusTimer);
}
statusTimer = setInterval(
() => (statusBarItem.text = statusBarItem.text + '.'),
1000
);
execution.processExitSubject.subscribe(data => {
if (statusTimer) {
clearInterval(statusTimer);
}
statusBarItem.hide();
});
statusBarItem.show();
}
}
export class CancellableStatusBar {
public static show(
execution: CommandExecution,
token: CancellationTokenSource
) {
return new CancellableStatusBar(execution, token);
}
private constructor(
execution: CommandExecution,
token: CancellationTokenSource
) {
resetStatusBarItem();
statusBarItem.text = `$(x) ${execution.command}`;
statusBarItem.tooltip = 'Click to cancel the command';
statusBarItem.command = CANCEL_EXECUTION_COMMAND;
cancellationTokenSource = token;
if (statusTimer) {
clearInterval(statusTimer);
}
statusTimer = setInterval(
() => (statusBarItem.text = statusBarItem.text + '.'),
1000
);
execution.processExitSubject.subscribe(data => {
if (statusTimer) {
clearInterval(statusTimer);
}
statusBarItem.hide();
});
statusBarItem.show();
}
}
function resetStatusBarItem() {
statusBarItem.text = '';
statusBarItem.tooltip = '';
statusBarItem.command = undefined;
}
export function cancelCommandExecution() {
if (cancellationTokenSource) {
cancellationTokenSource.cancel();
resetStatusBarItem();
if (statusTimer) {
clearInterval(statusTimer);
}
}
}