Skip to content

Commit

Permalink
Added silent kill switch so async tasks can end quietly (#12)
Browse files Browse the repository at this point in the history
  • Loading branch information
fg123 authored and MichaelKim committed May 2, 2018
1 parent 8332ad5 commit 1a59faa
Showing 2 changed files with 127 additions and 145 deletions.
38 changes: 19 additions & 19 deletions src/background/commands/kill.js
Original file line number Diff line number Diff line change
@@ -3,24 +3,24 @@
import type { StoreState } from 'types';

export default function kill(state: StoreState, params: Array<string>) {
if (params.length === 1) {
const index = parseInt(params[0]);
if (index >= 0 && index < this.workspace.windows.length) {
if (
this.workspace.windows[index].terminal.running ||
this.workspace.windows[index].terminal.isExtension
) {
this.workspace.windows[index].terminal.running = false;
this.workspace.windows[index].terminal.isExtension = false;
this.output('Extension killed');
} else {
this.output('Nothing running in window');
}
} else {
this.output('Invalid parameter');
const silent = params.includes('-s');
const newParams = params.filter(p => p !== '-s');
for (let i = 0; i < newParams.length; i++) {
const index = parseInt(newParams[i]);
if (index < 0 || index >= this.workspace.windows.length) {
this.output('Invalid parameter');
return state;
}
const terminal = this.workspace.windows[index].terminal;
if (terminal.running || terminal.isExtension) {
terminal.running = false;
terminal.isExtension = false;
if (!silent) {
this.output('Extension killed');
}
} else {
this.output('Nothing running in window');
}
}
} else {
this.output('Invalid number of parameters');
}
return state;
return state;
}
234 changes: 108 additions & 126 deletions src/background/scripts/index.js
Original file line number Diff line number Diff line change
@@ -2,153 +2,135 @@

import { isCommand } from 'background/commands';
import store from 'background/store';
import Constants, {
createFile,
createTerminal,
createWindow
} from 'constants.js';
import Constants, { createFile, createTerminal, createWindow } from 'constants.js';
import { findWindow, getDirectory, getFile, getPath } from 'utils';

import type { Directory, File, Script as ScriptType, Window } from 'types';

// Parse input into command and parameters
function parseInput(text): Array<string> {
const tokens = text.trim().match(/[^\s"']+|"([^"]*)"|'([^']*)'/g);
if (tokens)
return tokens.map(t => t.replace(/^"|"$/g, '').replace(/^'|'$/g, ''));
return [];
const tokens = text.trim().match(/[^\s"']+|"([^"]*)"|'([^']*)'/g);
if (tokens) return tokens.map(t => t.replace(/^"|"$/g, '').replace(/^'|'$/g, ''));
return [];
}

// List of built-in scripts (doesn't include .bin)
function isScript(name: string) {
const names = ['async', 'edit', 'yum'];
return names.find(n => n === name);
const names = ['async', 'edit', 'yum'];
return names.find(n => n === name);
}

function Script(command): ScriptType {
const workspaceID = store.getState().selectedWorkspace;
const selectedWindow = store.getState().selectedWindow;
const windowID = findWindow(store.getState(), selectedWindow);
const currentWindow = store.getState().workspaces[workspaceID].windows[
windowID
];
return {
getState: store.getState,
workspaceID,
windowID,
currentWindow,
currentPath: currentWindow.terminal.workingDirectory,
getFile: function(filePath: string, currentPath: string = '~') {
return getFile(filePath, store.getState().wfs, currentPath);
},
getDirectory: function(filePath: string, currentPath: string = '~') {
return getDirectory(filePath, store.getState().wfs, currentPath);
},
getPath: function(path: string, currentPath: string = '~') {
return getPath(path, store.getState().wfs, currentPath);
},
output: function(text, showPrompt = false, showCommand = true) {
store.dispatch({
type: 'ADD_COMMAND',
text: showCommand ? command + ': ' + text : text,
showPrompt
});
},
exec: function(input: string, callback?: any => void) {
const [cmd] = input.split(' ');
if (cmd && !isCommand(cmd)) {
executeScript(selectedWindow, input, callback);
} else {
store.dispatch({ type: 'EXECUTE_COMMAND', text: input, hidden: true });
if (callback) callback();
}
},
// resetStore: () => store.dispatch({ type: 'RESET_STORE' }),
// clearHistory: () => store.dispatch({ type: 'CLEAR_HISTORY' }),
// addCommand: (text: string, showPrompt: boolean) =>
// store.dispatch({ type: 'ADD_COMMAND', text, showPrompt }),
// setEnv: (key: string, value: string) =>
// store.dispatch({ type: 'SET_ENV', key, value }),
// setDirectory: (path: string) =>
// store.dispatch({
// type: 'SET_DIRECTORY',
// path,
// workspace: workspaceID,
// window: windowID
// }),
createFile: (name: string, data: string) => createFile(name, data),
createTerminal: () => createTerminal(),
createWindow: (x: number, y: number, w: number, h: number, id: number) =>
createWindow(x, y, w, h, id),
createDirectory: (path: string, contents: Array<File | Directory>) =>
store.dispatch({ type: 'CREATE_DIR', path, contents }),
deleteFile: (path: string) => store.dispatch({ type: 'DELETE_FILE', path }),
deleteDirectory: (path: string) =>
store.dispatch({ type: 'DELETE_DIR', path }),
selectWorkspace: (id: number) =>
store.dispatch({ type: 'SELECT_WORKSPACE', id }),
addWorkspace: (windows: Array<Window>) =>
store.dispatch({ type: 'ADD_WORKSPACE', windows }),
deleteWorkspace: (id: number) =>
store.dispatch({ type: 'DELETE_WORKSPACE', id }),
getFile: (path: string) => {
const file = getFile(path, store.getState().wfs);
if (file) return file;
// Backward compatibility
return false;
},
writeFile: (path: string, content: string) =>
store.dispatch({ type: 'CREATE_OR_MODIFY_FILE', path, content }),
runExtension: (name: string, params: Array<string>) =>
store.dispatch({ type: 'RUN_EXTENSION', name, params })
};
const workspaceID = store.getState().selectedWorkspace;
const selectedWindow = store.getState().selectedWindow;
const windowID = findWindow(store.getState(), selectedWindow);
const currentWindow = store.getState().workspaces[workspaceID].windows[windowID];
return {
getState: store.getState,
workspaceID,
windowID,
currentWindow,
currentPath: currentWindow.terminal.workingDirectory,
getFile: function(filePath: string, currentPath: string = '~') {
return getFile(filePath, store.getState().wfs, currentPath);
},
getDirectory: function(filePath: string, currentPath: string = '~') {
return getDirectory(filePath, store.getState().wfs, currentPath);
},
getPath: function(path: string, currentPath: string = '~') {
return getPath(path, store.getState().wfs, currentPath);
},
output: function(text, showPrompt = false, showCommand = true) {
store.dispatch({
type: 'ADD_COMMAND',
text: showCommand ? command + ': ' + text : text,
showPrompt
});
},
exec: function(input: string, callback?: any => void) {
const [cmd] = input.split(' ');
if (cmd && !isCommand(cmd)) {
executeScript(selectedWindow, input, callback);
} else {
store.dispatch({ type: 'EXECUTE_COMMAND', text: input, hidden: true });
if (callback) callback();
}
},
// resetStore: () => store.dispatch({ type: 'RESET_STORE' }),
// clearHistory: () => store.dispatch({ type: 'CLEAR_HISTORY' }),
// addCommand: (text: string, showPrompt: boolean) =>
// store.dispatch({ type: 'ADD_COMMAND', text, showPrompt }),
// setEnv: (key: string, value: string) =>
// store.dispatch({ type: 'SET_ENV', key, value }),
// setDirectory: (path: string) =>
// store.dispatch({
// type: 'SET_DIRECTORY',
// path,
// workspace: workspaceID,
// window: windowID
// }),
createFile: (name: string, data: string) => createFile(name, data),
createTerminal: () => createTerminal(),
createWindow: (x: number, y: number, w: number, h: number, id: number) => createWindow(x, y, w, h, id),
createDirectory: (path: string, contents: Array<File | Directory>) =>
store.dispatch({ type: 'CREATE_DIR', path, contents }),
deleteFile: (path: string) => store.dispatch({ type: 'DELETE_FILE', path }),
deleteDirectory: (path: string) => store.dispatch({ type: 'DELETE_DIR', path }),
selectWorkspace: (id: number) => store.dispatch({ type: 'SELECT_WORKSPACE', id }),
addWorkspace: (windows: Array<Window>) => store.dispatch({ type: 'ADD_WORKSPACE', windows }),
deleteWorkspace: (id: number) => store.dispatch({ type: 'DELETE_WORKSPACE', id }),
getFile: (path: string) => {
const file = getFile(path, store.getState().wfs);
if (file) return file;
// Backward compatibility
return false;
},
writeFile: (path: string, content: string) => store.dispatch({ type: 'CREATE_OR_MODIFY_FILE', path, content }),
runExtension: (name: string, params: Array<string>) => store.dispatch({ type: 'RUN_EXTENSION', name, params })
};
}

function Async(func, script, params, resolve?) {
function newResolve(...args: Array<any>) {
if (resolve) resolve(...args);
else script.exec('kill ' + script.windowID);
}
function newResolve(...args: Array<any>) {
if (resolve) resolve(...args);
else script.exec('kill -s ' + script.windowID);
}

const val = func(script, params, newResolve);
// If the script returns nothing, it's assumed that the script is synchronous
// and it will not call resolve to finish.
if (val === undefined) newResolve();
const val = func(script, params, newResolve);
// If the script returns nothing, it's assumed that the script is synchronous
// and it will not call resolve to finish.
if (val === undefined) newResolve();
}

export default function executeScript(
id: number,
input: string,
callback?: any => void
) {
// if (commands[id] && commands[id].running) return;
const [name, ...params] = parseInput(input);
const script = new Script(name);
export default function executeScript(id: number, input: string, callback?: any => void) {
// if (commands[id] && commands[id].running) return;
const [name, ...params] = parseInput(input);
const script = new Script(name);

const binFile = getFile('~/.bin/' + name, store.getState().wfs);
if (binFile) {
// $FlowFixMe: Flow doesn't like function objects
const binFunc = new Function('script', 'args', 'resolve', binFile.data);
try {
Async(binFunc, script, params, callback);
} catch (e) {
script.output('Error: ' + e);
script.exec('kill ' + script.windowID);
const binFile = getFile('~/.bin/' + name, store.getState().wfs);
if (binFile) {
// $FlowFixMe: Flow doesn't like function objects
const binFunc = new Function('script', 'args', 'resolve', binFile.data);
try {
Async(binFunc, script, params, callback);
} catch (e) {
script.output('Error: ' + e);
script.exec('kill ' + script.windowID);
}
} else if (isScript(name)) {
Async(require('./' + name).default, script, params, callback);
} else {
const path = Constants.MERCURYWM_CONTENT_URL + name + '/index.html';
fetch(path)
.then(response => {
if (response.ok) script.runExtension(name, params);
else throw 'Unrecognized command';
})
.catch(error => {
script.output(error);
script.exec('kill ' + script.windowID);
});
}
} else if (isScript(name)) {
Async(require('./' + name).default, script, params, callback);
} else {
const path = Constants.MERCURYWM_CONTENT_URL + name + '/index.html';
fetch(path)
.then(response => {
if (response.ok) script.runExtension(name, params);
else throw 'Unrecognized command';
})
.catch(error => {
script.output(error);
script.exec('kill ' + script.windowID);
});
}
}

/*

0 comments on commit 1a59faa

Please sign in to comment.