diff --git a/app/commands.js b/app/commands.js index 498573cba54d..2cdb6f9588a1 100644 --- a/app/commands.js +++ b/app/commands.js @@ -1,4 +1,5 @@ const {app} = require('electron'); +const uuid = require('uuid'); const {openConfig} = require('./config'); const {updatePlugins} = require('./plugins'); const {installCLI} = require('./utils/cli-install'); @@ -10,16 +11,16 @@ const commands = { }, 'tab:new': focusedWindow => { if (focusedWindow) { - focusedWindow.rpc.emit('termgroup add req'); + focusedWindow.rpc.emit('termgroup add req', {termGroupUid: uuid.v4(), sessionUid: uuid.v4()}); } else { setTimeout(app.createWindow, 0); } }, 'pane:splitVertical': focusedWindow => { - focusedWindow && focusedWindow.rpc.emit('split request vertical'); + focusedWindow && focusedWindow.rpc.emit('split request vertical', {sessionUid: uuid.v4()}); }, 'pane:splitHorizontal': focusedWindow => { - focusedWindow && focusedWindow.rpc.emit('split request horizontal'); + focusedWindow && focusedWindow.rpc.emit('split request horizontal', {sessionUid: uuid.v4()}); }, 'pane:close': focusedWindow => { focusedWindow && focusedWindow.rpc.emit('termgroup close req'); diff --git a/app/plugins.js b/app/plugins.js index b32e172144fd..c4e3eb56f17a 100644 --- a/app/plugins.js +++ b/app/plugins.js @@ -332,6 +332,19 @@ exports.onWindow = win => { }); }; +exports.extendSession = (window, opts, newSession) => { + modules.forEach(plugin => { + if (plugin.extendSession) { + try { + newSession = plugin.extendSession(window, opts, newSession); + } catch (e) { + notify('Plugin error!', `"${plugin._name}" has encountered an error. Check Developer Tools for details.`); + } + } + }); + return newSession; +}; + // decorates the base object by calling plugin[key] // for all the available plugins function decorateObject(base, key) { diff --git a/app/session.js b/app/session.js index 82fd69817a5f..d867221fe8e3 100644 --- a/app/session.js +++ b/app/session.js @@ -22,9 +22,12 @@ try { const envFromConfig = config.getConfig().env || {}; module.exports = class Session extends EventEmitter { - constructor({rows, cols: columns, cwd, shell, shellArgs}) { - const osLocale = require('os-locale'); + constructor() { super(); + } + + init({rows, cols: columns, cwd, shell, shellArgs}) { + const osLocale = require('os-locale'); const baseEnv = Object.assign( {}, process.env, @@ -68,7 +71,7 @@ module.exports = class Session extends EventEmitter { if (this.ended) { return; } - this.emit('data', decoder.write(data)); + this.read(decoder.write(data)); }); this.pty.on('exit', () => { @@ -85,6 +88,10 @@ module.exports = class Session extends EventEmitter { this.destroy(); } + read(data) { + this.emit('data', data); + } + write(data) { this.pty.write(data); } diff --git a/app/ui/window.js b/app/ui/window.js index ec2bc1975c42..a1bd39011f41 100644 --- a/app/ui/window.js +++ b/app/ui/window.js @@ -66,7 +66,7 @@ module.exports = class Window { // If no callback is passed to createWindow, // a new session will be created by default. if (!fn) { - fn = win => win.rpc.emit('termgroup add req'); + fn = win => win.rpc.emit('termgroup add req', {termGroupUid: uuid.v4(), sessionUid: uuid.v4()}); } // app.windowCallback is the createWindow callback @@ -99,7 +99,10 @@ module.exports = class Window { ); const initSession = (opts, fn_) => { - fn_(uuid.v4(), new Session(opts)); + const session = new Session(); + const newSession = app.plugins.extendSession(window, opts, session); + newSession.init(opts); + fn_(opts.sessionUid, newSession); }; initSession(sessionOpts, (uid, session) => { @@ -110,11 +113,13 @@ module.exports = class Window { uid, splitDirection: sessionOpts.splitDirection, shell: session.shell, - pid: session.pty.pid + pid: session.pty ? session.pty.pid : null, + termGroupUid: sessionOpts.termGroupUid, + activeUid: sessionOpts.activeUid }); session.on('data', data => { - rpc.emit('session data', uid + data); + rpc.emit('session data', {uid, data}); }); session.on('exit', () => { @@ -188,8 +193,8 @@ module.exports = class Window { const deleteSessions = () => { sessions.forEach((session, key) => { session.removeAllListeners(); - session.destroy(); sessions.delete(key); + session.destroy(); }); }; // we reset the rpc channel only upon diff --git a/lib/actions/sessions.js b/lib/actions/sessions.js index b48b9165aaca..146e444c8a5f 100644 --- a/lib/actions/sessions.js +++ b/lib/actions/sessions.js @@ -18,7 +18,7 @@ import { SESSION_SET_XTERM_TITLE } from '../constants/sessions'; -export function addSession({uid, shell, pid, cols, rows, splitDirection}) { +export function addSession({uid, shell, pid, cols, rows, splitDirection, termGroupUid, activeUid}) { return (dispatch, getState) => { const {sessions} = getState(); const now = Date.now(); @@ -30,8 +30,9 @@ export function addSession({uid, shell, pid, cols, rows, splitDirection}) { cols, rows, splitDirection, - activeUid: sessions.activeUid, - now + activeUid: activeUid ? activeUid : sessions.activeUid, + now, + termGroupUid }); }; } diff --git a/lib/actions/term-groups.js b/lib/actions/term-groups.js index 3c2940ac7b4a..454111df9329 100644 --- a/lib/actions/term-groups.js +++ b/lib/actions/term-groups.js @@ -12,14 +12,16 @@ import getRootGroups from '../selectors'; import {setActiveSession, ptyExitSession, userExitSession} from './sessions'; function requestSplit(direction) { - return () => (dispatch, getState) => { + return (sessionUid, activeUid) => (dispatch, getState) => { dispatch({ type: SESSION_REQUEST, effect: () => { - const {ui} = getState(); + const {ui, sessions} = getState(); rpc.emit('new', { splitDirection: direction, - cwd: ui.cwd + cwd: ui.cwd, + sessionUid, + activeUid: activeUid ? activeUid : sessions.activeUid }); } }); @@ -37,7 +39,7 @@ export function resizeTermGroup(uid, sizes) { }; } -export function requestTermGroup() { +export function requestTermGroup(termGroupUid, sessionUid) { return (dispatch, getState) => { dispatch({ type: TERM_GROUP_REQUEST, @@ -48,7 +50,9 @@ export function requestTermGroup() { isNewGroup: true, cols, rows, - cwd + cwd, + termGroupUid, + sessionUid }); } }); diff --git a/lib/index.js b/lib/index.js index 034523c80f37..b6cf99f9a315 100644 --- a/lib/index.js +++ b/lib/index.js @@ -44,10 +44,7 @@ rpc.on('session add', data => { store_.dispatch(sessionActions.addSession(data)); }); -rpc.on('session data', d => { - // the uid is a uuid v4 so it's 36 chars long - const uid = d.slice(0, 36); - const data = d.slice(36); +rpc.on('session data', ({uid, data}) => { store_.dispatch(sessionActions.addSessionData(uid, data)); }); @@ -103,16 +100,16 @@ rpc.on('session break req', () => { store_.dispatch(sessionActions.sendSessionData(null, '\x03')); }); -rpc.on('termgroup add req', () => { - store_.dispatch(termGroupActions.requestTermGroup()); +rpc.on('termgroup add req', ({termGroupUid, sessionUid}) => { + store_.dispatch(termGroupActions.requestTermGroup(termGroupUid, sessionUid)); }); -rpc.on('split request horizontal', () => { - store_.dispatch(termGroupActions.requestHorizontalSplit()); +rpc.on('split request horizontal', ({sessionUid, activeUid}) => { + store_.dispatch(termGroupActions.requestHorizontalSplit(sessionUid, activeUid)); }); -rpc.on('split request vertical', () => { - store_.dispatch(termGroupActions.requestVerticalSplit()); +rpc.on('split request vertical', ({sessionUid, activeUid}) => { + store_.dispatch(termGroupActions.requestVerticalSplit(sessionUid, activeUid)); }); rpc.on('reset fontSize req', () => { diff --git a/lib/reducers/term-groups.js b/lib/reducers/term-groups.js index ad14152e8fe1..c77f859c9b73 100644 --- a/lib/reducers/term-groups.js +++ b/lib/reducers/term-groups.js @@ -192,7 +192,7 @@ const reducer = (state = initialState, action) => { return setActiveGroup(state, action); } - const uid = uuid.v4(); + const uid = action.termGroupUid; const termGroup = TermGroup({ uid, sessionUid: action.uid