Skip to content

Commit

Permalink
split panes: create initial implementation
Browse files Browse the repository at this point in the history
This allows users to split their Hyperterm terms into
multiple nested splits, both vertical and horizontal.

Fixes vercel#56
  • Loading branch information
ekmartin committed Aug 18, 2016
1 parent 5a566ae commit 98f1ca6
Show file tree
Hide file tree
Showing 23 changed files with 620 additions and 150 deletions.
14 changes: 8 additions & 6 deletions app/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ app.on('ready', () => {

// If no callback is passed to createWindow,
// a new session will be created by default.
if (!fn) fn = (win) => win.rpc.emit('session add req');
if (!fn) fn = (win) => win.rpc.emit('termgroup add req');

// app.windowCallback is the createWindow callback
// that can be setted before the 'ready' app event
Expand All @@ -144,14 +144,17 @@ app.on('ready', () => {
}
});

rpc.on('new', ({ rows = 40, cols = 100, cwd = process.env.HOME }) => {
rpc.on('new', ({ rows = 40, cols = 100, cwd = process.env.HOME, splitDirection }) => {
const shell = cfg.shell;
const shellArgs = cfg.shellArgs;

initSession({ rows, cols, cwd, shell, shellArgs }, (uid, session) => {
sessions.set(uid, session);
rpc.emit('session add', {
rows,
cols,
uid,
splitDirection,
shell: session.shell,
pid: session.pty.pid
});
Expand Down Expand Up @@ -214,10 +217,9 @@ app.on('ready', () => {
win.maximize();
});

rpc.on('resize', ({ cols, rows }) => {
sessions.forEach((session) => {
session.resize({ cols, rows });
});
rpc.on('resize', ({ uid, cols, rows }) => {
const session = sessions.get(uid);
session.resize({ cols, rows });
});

rpc.on('data', ({ uid, data }) => {
Expand Down
23 changes: 22 additions & 1 deletion app/menu.js
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ module.exports = function createMenu ({ createWindow, updatePlugins }) {
accelerator: 'CmdOrCtrl+T',
click (item, focusedWindow) {
if (focusedWindow) {
focusedWindow.rpc.emit('session add req');
focusedWindow.rpc.emit('termgroup add req');
} else {
createWindow();
}
Expand All @@ -79,6 +79,27 @@ module.exports = function createMenu ({ createWindow, updatePlugins }) {
{
type: 'separator'
},
{
label: 'Split Vertically',
accelerator: 'CmdOrCtrl+D',
click (item, focusedWindow) {
if (focusedWindow) {
focusedWindow.rpc.emit('split request vertical');
}
}
},
{
label: 'Split Horizontally',
accelerator: 'CmdOrCtrl+Shift+D',
click (item, focusedWindow) {
if (focusedWindow) {
focusedWindow.rpc.emit('split request horizontal');
}
}
},
{
type: 'separator'
},
{
label: 'Close',
accelerator: 'CmdOrCtrl+W',
Expand Down
5 changes: 3 additions & 2 deletions lib/actions/header.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { CLOSE_TAB, CHANGE_TAB } from '../constants/tabs';
import { UI_WINDOW_MAXIMIZE, UI_WINDOW_UNMAXIMIZE } from '../constants/ui';
import { userExitSession, setActiveSession } from './sessions';
import rpc from '../rpc';
import { userExitSession } from './sessions';
import { setActiveGroup } from './term-groups';

export function closeTab (uid) {
return (dispatch, getState) => {
Expand All @@ -21,7 +22,7 @@ export function changeTab (uid) {
type: CHANGE_TAB,
uid,
effect () {
dispatch(setActiveSession(uid));
dispatch(setActiveGroup(uid));
}
});
};
Expand Down
38 changes: 28 additions & 10 deletions lib/actions/sessions.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import rpc from '../rpc';
import getURL from '../utils/url-command';
import { keys } from '../utils/object';
import { findBySession } from '../utils/term-groups';
import {
SESSION_ADD,
SESSION_RESIZE,
Expand All @@ -19,18 +20,28 @@ import {
SESSION_SET_PROCESS_TITLE
} from '../constants/sessions';

export function addSession (uid, shell, pid) {
export function addSession (uid, shell, pid, cols, rows, splitDirection) {
return (dispatch, getState) => {
const { sessions } = getState();
// normally this would be encoded as an effect
// but the `SESSION_ADD` action is pretty expensive
// and we want to get this out as soon as possible
const { activeUid } = sessions;
dispatch({
type: SESSION_ADD,
uid,
shell,
pid
pid,
cols,
rows,
// TODO: These are a bit out of place:
activeUid,
splitDirection
});
};
}

export function requestSession (uid) {
export function requestSession () {
return (dispatch, getState) => {
const { ui } = getState();
const { cols, rows, cwd } = ui;
Expand Down Expand Up @@ -161,13 +172,20 @@ export function setSessionXtermTitle (uid, title) {
}

export function resizeSession (uid, cols, rows) {
return {
type: SESSION_RESIZE,
cols,
rows,
effect () {
rpc.emit('resize', { cols, rows });
}
return (dispatch, getState) => {
const { termGroups } = getState();
const group = findBySession(termGroups, uid);
const isStandaloneTerm = !group.parentUid && !group.children.length;
dispatch({
type: SESSION_RESIZE,
uid,
cols,
rows,
isStandaloneTerm,
effect () {
rpc.emit('resize', { uid, cols, rows });
}
});
};
}

Expand Down
64 changes: 64 additions & 0 deletions lib/actions/term-groups.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import rpc from '../rpc';
import {
DIRECTION,
TERM_GROUP_REQUEST,
TERM_GROUP_SPLIT
} from '../constants/term-groups';
import { SESSION_REQUEST } from '../constants/sessions';
import { setActiveSession } from './sessions';

function requestSplit (direction) {
return (dispatch, getState) => {
const { ui } = getState();
dispatch({
type: SESSION_REQUEST,
effect: () => {
rpc.emit('new', {
splitDirection: direction,
cwd: ui.cwd
});
}
});
};
}

export const requestVerticalSplit = () => requestSplit(DIRECTION.VERTICAL);
export const requestHorizontalSplit = () => requestSplit(DIRECTION.HORIZONTAL);

export function createSplit (uid, direction) {
return (dispatch, getState) => {
const { sessions } = getState();
dispatch({
type: TERM_GROUP_SPLIT,
activeUid: sessions.activeUid,
direction,
uid
});
};
}

export function requestTermGroup () {
return (dispatch, getState) => {
const { ui } = getState();
const { cols, rows, cwd } = ui;
dispatch({
type: TERM_GROUP_REQUEST,
effect: () => {
rpc.emit('new', {
isNewGroup: true,
cols,
rows,
cwd
});
}
});
};
}

export function setActiveGroup (uid) {
return (dispatch, getState) => {
const { termGroups } = getState();
const group = termGroups.termGroups[uid];
dispatch(setActiveSession(group.activeSessionUid));
};
}
44 changes: 24 additions & 20 deletions lib/actions/ui.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import * as shellEscape from 'php-escape-shell';
import { setActiveSession } from './sessions';
import { keys } from '../utils/object';
import { last } from '../utils/array';
import { isExecutable } from '../utils/file';
import { setActiveGroup } from './term-groups';
import { getRootGroups } from '../selectors';
import notify from '../utils/notify';
import rpc from '../rpc';
import {
Expand Down Expand Up @@ -86,15 +86,16 @@ export function moveLeft () {
dispatch({
type: UI_MOVE_LEFT,
effect () {
const { sessions } = getState();
const uid = sessions.activeUid;
const sessionUids = keys(sessions.sessions);
const index = sessionUids.indexOf(uid);
const next = sessionUids[index - 1] || last(sessionUids);
const state = getState();
const rootGroups = getRootGroups(state);
const groupUids = rootGroups.map(({ uid }) => uid);
const uid = state.termGroups.activeRootGroup;
const index = groupUids.indexOf(uid);
const next = groupUids[index - 1] || last(groupUids);
if (!next || uid === next) {
console.log('ignoring left move action');
} else {
dispatch(setActiveSession(next));
dispatch(setActiveGroup(next));
}
}
});
Expand All @@ -106,15 +107,16 @@ export function moveRight () {
dispatch({
type: UI_MOVE_RIGHT,
effect () {
const { sessions } = getState();
const uid = sessions.activeUid;
const sessionUids = keys(sessions.sessions);
const index = sessionUids.indexOf(uid);
const next = sessionUids[index + 1] || sessionUids[0];
const state = getState();
const rootGroups = getRootGroups(state);
const groupUids = rootGroups.map(({ uid }) => uid);
const uid = state.termGroups.activeRootGroup;
const index = groupUids.indexOf(uid);
const next = groupUids[index + 1] || groupUids[0];
if (!next || uid === next) {
console.log('ignoring right move action');
} else {
dispatch(setActiveSession(next));
dispatch(setActiveGroup(next));
}
}
});
Expand All @@ -127,13 +129,15 @@ export function moveTo (i) {
type: UI_MOVE_TO,
index: i,
effect () {
const { sessions } = getState();
const uid = sessions.activeUid;
const sessionUids = keys(sessions.sessions);
if (uid === sessionUids[i]) {
const state = getState();
const rootGroups = getRootGroups(state);
const groupUids = rootGroups.map(({ uid }) => uid);
const uid = state.termGroups.activeRootGroup;
if (uid === groupUids[i]) {
console.log('ignoring same uid');
} else if (null != sessionUids[i]) {
dispatch(setActiveSession(sessionUids[i]));
} else if (null != groupUids[i]) {
console.log('dispatching set group', groupUids[i]);
dispatch(setActiveGroup(groupUids[i]));
} else {
console.log('ignoring inexistent index', i);
}
Expand Down

0 comments on commit 98f1ca6

Please sign in to comment.