mirror of
https://github.com/quine-global/hyper.git
synced 2026-01-12 20:18:41 -09:00
Add perf optimizations (#1541)
* pass `uid` to term * term: keep track of term instance references * sessions: remvoe `write` object overhead * hterm: cache measurement of codepoints for single char strings * sessions: merge less eagerly when we receive a PTY_DATA action * store: handle the side effect of writing to the terminal from a middleware * terms: add terms instance cache * lint
This commit is contained in:
parent
1cd2620da0
commit
dc3b90028b
9 changed files with 54 additions and 26 deletions
|
|
@ -76,7 +76,8 @@ class TermGroup_ extends Component {
|
||||||
onData: this.bind(this.props.onData, null, uid),
|
onData: this.bind(this.props.onData, null, uid),
|
||||||
onURLAbort: this.bind(this.props.onURLAbort, null, uid),
|
onURLAbort: this.bind(this.props.onURLAbort, null, uid),
|
||||||
borderColor: this.props.borderColor,
|
borderColor: this.props.borderColor,
|
||||||
quickEdit: this.props.quickEdit
|
quickEdit: this.props.quickEdit,
|
||||||
|
uid
|
||||||
});
|
});
|
||||||
|
|
||||||
// This will create a new ref_ function for every render,
|
// This will create a new ref_ function for every render,
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ import uuid from 'uuid';
|
||||||
import hterm from '../hterm';
|
import hterm from '../hterm';
|
||||||
import Component from '../component';
|
import Component from '../component';
|
||||||
import getColorList from '../utils/colors';
|
import getColorList from '../utils/colors';
|
||||||
|
import terms from '../terms';
|
||||||
import notify from '../utils/notify';
|
import notify from '../utils/notify';
|
||||||
|
|
||||||
export default class Term extends Component {
|
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('mouseup', this.handleMouseUp);
|
||||||
this.getScreenNode().addEventListener('mousedown', this.handleMouseDown);
|
this.getScreenNode().addEventListener('mousedown', this.handleMouseDown);
|
||||||
|
terms[this.props.uid] = this;
|
||||||
}
|
}
|
||||||
|
|
||||||
handleWheel(e) {
|
handleWheel(e) {
|
||||||
|
|
@ -364,6 +366,7 @@ export default class Term extends Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillUnmount() {
|
componentWillUnmount() {
|
||||||
|
terms[this.props.uid] = this;
|
||||||
// turn blinking off to prevent leaking a timeout when disposing terminal
|
// turn blinking off to prevent leaking a timeout when disposing terminal
|
||||||
const prefs = this.term.getPrefs();
|
const prefs = this.term.getPrefs();
|
||||||
prefs.set('cursor-blink', false);
|
prefs.set('cursor-blink', false);
|
||||||
|
|
|
||||||
|
|
@ -16,13 +16,6 @@ export default class Terms extends Component {
|
||||||
props.ref_(this);
|
props.ref_(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillReceiveProps(next) {
|
|
||||||
const {write} = next;
|
|
||||||
if (write && this.props.write !== write) {
|
|
||||||
this.getTermByUid(write.uid).write(write.data);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
shouldComponentUpdate(nextProps) {
|
shouldComponentUpdate(nextProps) {
|
||||||
for (const i in nextProps) {
|
for (const i in nextProps) {
|
||||||
if (i === 'write') {
|
if (i === 'write') {
|
||||||
|
|
|
||||||
|
|
@ -57,7 +57,12 @@ hterm.TextAttributes.splitWidecharString = function (str) {
|
||||||
};
|
};
|
||||||
|
|
||||||
// hterm Unicode patch
|
// hterm Unicode patch
|
||||||
|
const cache = [];
|
||||||
lib.wc.strWidth = function (str) {
|
lib.wc.strWidth = function (str) {
|
||||||
|
const shouldCache = str.length === 1;
|
||||||
|
if (shouldCache && cache[str] !== undefined) {
|
||||||
|
return cache[str];
|
||||||
|
}
|
||||||
const chars = runes(str);
|
const chars = runes(str);
|
||||||
let width = 0;
|
let width = 0;
|
||||||
let rv = 0;
|
let rv = 0;
|
||||||
|
|
@ -70,6 +75,9 @@ lib.wc.strWidth = function (str) {
|
||||||
}
|
}
|
||||||
rv += width * ((codePoint <= 0xffff) ? 1 : 2);
|
rv += width * ((codePoint <= 0xffff) ? 1 : 2);
|
||||||
}
|
}
|
||||||
|
if (shouldCache) {
|
||||||
|
cache[str] = rv;
|
||||||
|
}
|
||||||
return rv;
|
return rv;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,6 @@ import {
|
||||||
|
|
||||||
const initialState = Immutable({
|
const initialState = Immutable({
|
||||||
sessions: {},
|
sessions: {},
|
||||||
write: null,
|
|
||||||
activeUid: null
|
activeUid: null
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -26,7 +25,6 @@ function Session(obj) {
|
||||||
title: '',
|
title: '',
|
||||||
cols: null,
|
cols: null,
|
||||||
rows: null,
|
rows: null,
|
||||||
write: null,
|
|
||||||
url: null,
|
url: null,
|
||||||
cleared: false,
|
cleared: false,
|
||||||
shell: '',
|
shell: '',
|
||||||
|
|
@ -34,13 +32,6 @@ function Session(obj) {
|
||||||
}).merge(obj);
|
}).merge(obj);
|
||||||
}
|
}
|
||||||
|
|
||||||
function Write(obj) {
|
|
||||||
return Immutable({
|
|
||||||
uid: '',
|
|
||||||
data: ''
|
|
||||||
}).merge(obj);
|
|
||||||
}
|
|
||||||
|
|
||||||
const reducer = (state = initialState, action) => {
|
const reducer = (state = initialState, action) => {
|
||||||
switch (action.type) {
|
switch (action.type) {
|
||||||
case SESSION_ADD:
|
case SESSION_ADD:
|
||||||
|
|
@ -73,15 +64,20 @@ const reducer = (state = initialState, action) => {
|
||||||
}, {deep: true});
|
}, {deep: true});
|
||||||
|
|
||||||
case SESSION_PTY_DATA:
|
case SESSION_PTY_DATA:
|
||||||
return state
|
// we avoid a direct merge for perf reasons
|
||||||
.set('write', Write(action))
|
// as this is the most common action
|
||||||
.merge({
|
if (state.sessions[action.uid] &&
|
||||||
sessions: {
|
state.sessions[action.uid].cleared) {
|
||||||
[action.uid]: {
|
return state
|
||||||
cleared: false
|
.merge({
|
||||||
|
sessions: {
|
||||||
|
[action.uid]: {
|
||||||
|
cleared: false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}, {deep: true});
|
||||||
}, {deep: true});
|
}
|
||||||
|
return state;
|
||||||
|
|
||||||
case SESSION_PTY_EXIT:
|
case SESSION_PTY_EXIT:
|
||||||
if (state.sessions[action.uid]) {
|
if (state.sessions[action.uid]) {
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ import createLogger from 'redux-logger';
|
||||||
import rootReducer from '../reducers/index';
|
import rootReducer from '../reducers/index';
|
||||||
import effects from '../utils/effects';
|
import effects from '../utils/effects';
|
||||||
import * as plugins from '../utils/plugins';
|
import * as plugins from '../utils/plugins';
|
||||||
|
import writeMiddleware from './write-middleware';
|
||||||
|
|
||||||
export default () => {
|
export default () => {
|
||||||
const logger = createLogger({
|
const logger = createLogger({
|
||||||
|
|
@ -17,6 +18,7 @@ export default () => {
|
||||||
plugins.middleware,
|
plugins.middleware,
|
||||||
thunk,
|
thunk,
|
||||||
effects,
|
effects,
|
||||||
|
writeMiddleware,
|
||||||
logger
|
logger
|
||||||
),
|
),
|
||||||
window.devToolsExtension()
|
window.devToolsExtension()
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ import thunk from 'redux-thunk';
|
||||||
import rootReducer from '../reducers/index';
|
import rootReducer from '../reducers/index';
|
||||||
import effects from '../utils/effects';
|
import effects from '../utils/effects';
|
||||||
import * as plugins from '../utils/plugins';
|
import * as plugins from '../utils/plugins';
|
||||||
|
import writeMiddleware from './write-middleware';
|
||||||
|
|
||||||
export default () =>
|
export default () =>
|
||||||
createStore(
|
createStore(
|
||||||
|
|
@ -11,6 +12,7 @@ export default () =>
|
||||||
thunk,
|
thunk,
|
||||||
plugins.middleware,
|
plugins.middleware,
|
||||||
thunk,
|
thunk,
|
||||||
effects
|
effects,
|
||||||
|
writeMiddleware
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
|
||||||
14
lib/store/write-middleware.js
Normal file
14
lib/store/write-middleware.js
Normal file
|
|
@ -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);
|
||||||
|
};
|
||||||
9
lib/terms.js
Normal file
9
lib/terms.js
Normal file
|
|
@ -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;
|
||||||
Loading…
Reference in a new issue