diff --git a/lib/components/term-group.js b/lib/components/term-group.js index 7d751877f93c..d28c89eccda4 100644 --- a/lib/components/term-group.js +++ b/lib/components/term-group.js @@ -76,7 +76,8 @@ class TermGroup_ extends Component { onData: this.bind(this.props.onData, null, uid), onURLAbort: this.bind(this.props.onURLAbort, null, uid), borderColor: this.props.borderColor, - quickEdit: this.props.quickEdit + quickEdit: this.props.quickEdit, + uid }); // This will create a new ref_ function for every render, diff --git a/lib/components/term.js b/lib/components/term.js index 46e9ddcd3433..a11c9d64b544 100644 --- a/lib/components/term.js +++ b/lib/components/term.js @@ -5,6 +5,7 @@ import uuid from 'uuid'; import hterm from '../hterm'; import Component from '../component'; import getColorList from '../utils/colors'; +import terms from '../terms'; import notify from '../utils/notify'; export default class Term extends Component { @@ -97,6 +98,7 @@ export default class Term extends Component { this.getScreenNode().addEventListener('mouseup', this.handleMouseUp); this.getScreenNode().addEventListener('mousedown', this.handleMouseDown); + terms[this.props.uid] = this; } handleWheel(e) { @@ -364,6 +366,7 @@ export default class Term extends Component { } componentWillUnmount() { + terms[this.props.uid] = this; // turn blinking off to prevent leaking a timeout when disposing terminal const prefs = this.term.getPrefs(); prefs.set('cursor-blink', false); diff --git a/lib/components/terms.js b/lib/components/terms.js index b20bc1d5733b..f48f2688caa0 100644 --- a/lib/components/terms.js +++ b/lib/components/terms.js @@ -16,13 +16,6 @@ export default class Terms extends Component { props.ref_(this); } - componentWillReceiveProps(next) { - const {write} = next; - if (write && this.props.write !== write) { - this.getTermByUid(write.uid).write(write.data); - } - } - shouldComponentUpdate(nextProps) { for (const i in nextProps) { if (i === 'write') { diff --git a/lib/hterm.js b/lib/hterm.js index 442c21dc4d4c..4be1640460cd 100644 --- a/lib/hterm.js +++ b/lib/hterm.js @@ -57,7 +57,12 @@ hterm.TextAttributes.splitWidecharString = function (str) { }; // hterm Unicode patch +const cache = []; lib.wc.strWidth = function (str) { + const shouldCache = str.length === 1; + if (shouldCache && cache[str] !== undefined) { + return cache[str]; + } const chars = runes(str); let width = 0; let rv = 0; @@ -70,6 +75,9 @@ lib.wc.strWidth = function (str) { } rv += width * ((codePoint <= 0xffff) ? 1 : 2); } + if (shouldCache) { + cache[str] = rv; + } return rv; }; diff --git a/lib/reducers/sessions.js b/lib/reducers/sessions.js index 2484535e4453..85043f91c6a3 100644 --- a/lib/reducers/sessions.js +++ b/lib/reducers/sessions.js @@ -16,7 +16,6 @@ import { const initialState = Immutable({ sessions: {}, - write: null, activeUid: null }); @@ -26,7 +25,6 @@ function Session(obj) { title: '', cols: null, rows: null, - write: null, url: null, cleared: false, shell: '', @@ -34,13 +32,6 @@ function Session(obj) { }).merge(obj); } -function Write(obj) { - return Immutable({ - uid: '', - data: '' - }).merge(obj); -} - const reducer = (state = initialState, action) => { switch (action.type) { case SESSION_ADD: @@ -73,15 +64,20 @@ const reducer = (state = initialState, action) => { }, {deep: true}); case SESSION_PTY_DATA: - return state - .set('write', Write(action)) - .merge({ - sessions: { - [action.uid]: { - cleared: false + // we avoid a direct merge for perf reasons + // as this is the most common action + if (state.sessions[action.uid] && + state.sessions[action.uid].cleared) { + return state + .merge({ + sessions: { + [action.uid]: { + cleared: false + } } - } - }, {deep: true}); + }, {deep: true}); + } + return state; case SESSION_PTY_EXIT: if (state.sessions[action.uid]) { diff --git a/lib/store/configure-store.dev.js b/lib/store/configure-store.dev.js index a00e8ff14938..7b3bf57a42a0 100644 --- a/lib/store/configure-store.dev.js +++ b/lib/store/configure-store.dev.js @@ -4,6 +4,7 @@ import createLogger from 'redux-logger'; import rootReducer from '../reducers/index'; import effects from '../utils/effects'; import * as plugins from '../utils/plugins'; +import writeMiddleware from './write-middleware'; export default () => { const logger = createLogger({ @@ -17,6 +18,7 @@ export default () => { plugins.middleware, thunk, effects, + writeMiddleware, logger ), window.devToolsExtension() diff --git a/lib/store/configure-store.prod.js b/lib/store/configure-store.prod.js index af230840938f..fe8acd4a62f3 100644 --- a/lib/store/configure-store.prod.js +++ b/lib/store/configure-store.prod.js @@ -3,6 +3,7 @@ import thunk from 'redux-thunk'; import rootReducer from '../reducers/index'; import effects from '../utils/effects'; import * as plugins from '../utils/plugins'; +import writeMiddleware from './write-middleware'; export default () => createStore( @@ -11,6 +12,7 @@ export default () => thunk, plugins.middleware, thunk, - effects + effects, + writeMiddleware ) ); diff --git a/lib/store/write-middleware.js b/lib/store/write-middleware.js new file mode 100644 index 000000000000..72372ecb7704 --- /dev/null +++ b/lib/store/write-middleware.js @@ -0,0 +1,14 @@ +import terms from '../terms'; + +// the only side effect we perform from middleware +// is to write to the react term instance directly +// to avoid a performance hit +export default () => next => action => { + if (action.type === 'SESSION_PTY_DATA') { + const term = terms[action.uid]; + if (term) { + term.write(action.data); + } + } + next(action); +}; diff --git a/lib/terms.js b/lib/terms.js new file mode 100644 index 000000000000..ce4b6ad476eb --- /dev/null +++ b/lib/terms.js @@ -0,0 +1,9 @@ +// react Term components add themselves +// to this object upon mounting / unmounting +// this is to allow imperative access to the +// term API, which is a performance +// optimization for the most common action +// within the system + +const terms = {}; +export default terms;