Xterm v3 integration (#2573)

This commit is contained in:
CHaBou 2018-01-09 17:33:24 +01:00 committed by GitHub
parent 5700690e0b
commit dd780e6fe7
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 310 additions and 2365 deletions

File diff suppressed because it is too large Load diff

View file

@ -59,8 +59,13 @@ class TermGroup_ extends PureComponent {
const props = getTermProps(uid, this.props, {
isTermActive: uid === this.props.activeSession,
term: termRef ? termRef.term : null,
backgroundColor: this.props.backgroundColor,
foregroundColor: this.props.foregroundColor,
colors: this.props.colors,
cursorBlink: this.props.cursorBlink,
cursorShape: this.props.cursorShape,
cursorColor: this.props.cursorColor,
cursorAccentColor: this.props.cursorAccentColor,
fontSize: this.props.fontSize,
fontFamily: this.props.fontFamily,
uiFontFamily: this.props.uiFontFamily,
@ -81,6 +86,7 @@ class TermGroup_ extends PureComponent {
onURLAbort: this.bind(this.props.onURLAbort, null, uid),
onContextMenu: this.bind(this.props.onContextMenu, null, uid),
borderColor: this.props.borderColor,
selectionColor: this.props.selectionColor,
quickEdit: this.props.quickEdit,
uid
});

View file

@ -1,7 +1,8 @@
/* global Blob,URL,requestAnimationFrame */
import React from 'react';
import Terminal from 'xterm';
import {Terminal} from 'xterm';
import {clipboard} from 'electron';
import * as Color from 'color';
import {PureComponent} from '../base-components';
import terms from '../terms';
import processClipboard from '../utils/paste';
@ -13,6 +14,41 @@ const CURSOR_STYLES = {
BLOCK: 'block'
};
const getTermOptions = props => {
// Set a background color only if it is opaque
const backgroundColor = Color(props.backgroundColor).alpha() < 1 ? 'transparent' : props.backgroundColor;
return {
cursorStyle: CURSOR_STYLES[props.cursorShape],
cursorBlink: props.cursorBlink,
fontFamily: props.fontFamily,
fontSize: props.fontSize,
allowTransparency: true,
theme: {
foreground: props.foregroundColor,
background: backgroundColor,
cursor: props.cursorColor,
cursorAccent: props.cursorAccentColor,
selection: props.selectionColor,
black: props.colors.black,
red: props.colors.red,
green: props.colors.green,
yellow: props.colors.yellow,
blue: props.colors.blue,
magenta: props.colors.magenta,
cyan: props.colors.cyan,
white: props.colors.white,
brightBlack: props.colors.lightBlack,
brightRed: props.colors.lightRed,
brightGreen: props.colors.lightGreen,
brightYellow: props.colors.lightYellow,
brightBlue: props.colors.lightBlue,
brightMagenta: props.colors.lightMagenta,
brightCyan: props.colors.lightCyan,
brightWhite: props.colors.lightWhite
}
};
};
export default class Term extends PureComponent {
constructor(props) {
super(props);
@ -26,30 +62,23 @@ export default class Term extends PureComponent {
this.onTermRef = this.onTermRef.bind(this);
this.onTermWrapperRef = this.onTermWrapperRef.bind(this);
this.onMouseUp = this.onMouseUp.bind(this);
this.termOptions = {};
}
componentDidMount() {
const {props} = this;
// we need to use this hack to retain the term reference
// as we move the term around splits, until xterm adds
// support for getState / setState
this.termOptions = getTermOptions(props);
this.term = props.term || new Terminal(this.termOptions);
this.term.attachCustomKeyEventHandler(this.keyboardHandler);
this.term.open(this.termRef);
if (props.term) {
this.term = props.term;
this.termRef.appendChild(this.term.element);
this.onOpen();
} else {
this.term =
props.term ||
new Terminal({
cursorStyle: CURSOR_STYLES[props.cursorShape],
cursorBlink: props.cursorBlink
});
this.term.attachCustomKeyEventHandler(this.keyboardHandler);
this.term.open(this.termRef);
this.onOpen();
//We need to set options again after reattaching an existing term
Object.keys(this.termOptions).forEach(option => this.term.setOption(option, this.termOptions[option]));
}
this.onOpen(this.termOptions);
if (props.onTitle) {
this.term.on('title', props.onTitle);
}
@ -84,7 +113,7 @@ export default class Term extends PureComponent {
terms[this.props.uid] = this;
}
onOpen() {
onOpen(termOptions) {
// we need to delay one frame so that aphrodite styles
// get applied and we can make an accurate measurement
// of the container width and height
@ -93,7 +122,9 @@ export default class Term extends PureComponent {
// measurement to have taken place but it seems that
// xterm.js might be doing this asynchronously, so
// we force it instead
this.term.charMeasure.measure();
// eslint-disable-next-line no-debugger
//debugger;
this.term.charMeasure.measure(termOptions);
this.fitResize();
});
}
@ -179,10 +210,28 @@ export default class Term extends PureComponent {
if (!this.props.cleared && nextProps.cleared) {
this.clear();
}
const nextTermOptions = getTermOptions(nextProps);
// Update only options that have changed.
Object.keys(nextTermOptions)
.filter(option => option !== 'theme' && nextTermOptions[option] !== this.termOptions[option])
.forEach(option => this.term.setOption(option, nextTermOptions[option]));
// Do we need to update theme?
const shouldUpdateTheme =
!this.termOptions.theme ||
Object.keys(nextTermOptions.theme).some(option => {
nextTermOptions.theme[option] !== this.termOptions.theme[option];
});
if (shouldUpdateTheme) {
this.term.setOption('theme', nextTermOptions.theme);
}
this.termOptions = nextTermOptions;
if (!this.props.isTermActive && nextProps.isTermActive) {
requestAnimationFrame(() => {
this.term.charMeasure.measure();
this.term.charMeasure.measure(this.termOptions);
this.fitResize();
});
}
@ -190,7 +239,7 @@ export default class Term extends PureComponent {
if (this.props.fontSize !== nextProps.fontSize || this.props.fontFamily !== nextProps.fontFamily) {
// invalidate xterm cache about how wide each
// character is
this.term.charMeasure.measure();
this.term.charMeasure.measure(this.termOptions);
// resize to fit the container
this.fitResize();

View file

@ -90,9 +90,13 @@ export default class Terms extends Component {
activeSession: this.props.activeSession,
sessions: this.props.sessions,
backgroundColor: this.props.backgroundColor,
foregroundColor: this.props.foregroundColor,
borderColor: this.props.borderColor,
selectionColor: this.props.selectionColor,
colors: this.props.colors,
cursorShape: this.props.cursorShape,
cursorBlink: this.props.cursorBlink,
cursorColor: this.props.cursorColor,
fontSize: this.props.fontSize,
fontFamily: this.props.fontFamily,
uiFontFamily: this.props.uiFontFamily,
@ -119,13 +123,9 @@ export default class Terms extends Component {
})}
{this.props.customChildren}
<StyleSheet
colors={this.props.colors}
backgroundColor={this.props.backgroundColor}
customCSS={this.props.customCSS}
cursorColor={this.props.cursorColor}
fontSize={this.props.fontSize}
fontFamily={this.props.fontFamily}
fontSmoothing={this.props.fontSmoothing}
foregroundColor={this.props.foregroundColor}
borderColor={this.props.borderColor}
/>

View file

@ -28,9 +28,11 @@ const TermsContainer = connect(
fontSmoothing: state.ui.fontSmoothingOverride,
padding: state.ui.padding,
cursorColor: state.ui.cursorColor,
cursorAccentColor: state.ui.cursorAccentColor,
cursorShape: state.ui.cursorShape,
cursorBlink: state.ui.cursorBlink,
borderColor: state.ui.borderColor,
selectionColor: state.ui.selectionColor,
colors: state.ui.colors,
foregroundColor: state.ui.foregroundColor,
backgroundColor: state.ui.backgroundColor,

View file

@ -34,9 +34,11 @@ const initial = Immutable({
rows: null,
activeUid: null,
cursorColor: '#F81CE5',
cursorAccentColor: '#000',
cursorShape: 'BLOCK',
cursorBlink: false,
borderColor: '#333',
selectionColor: 'rgba(248,28,229,0.3)',
fontSize: 12,
padding: '12px 14px',
fontFamily: 'Menlo, "DejaVu Sans Mono", "Lucida Console", monospace',
@ -131,6 +133,10 @@ const reducer = (state = initial, action) => {
ret.cursorColor = config.cursorColor;
}
if (config.cursorAccentColor) {
ret.cursorAccentColor = config.cursorAccentColor;
}
if (allowedCursorShapes.has(config.cursorShape)) {
ret.cursorShape = config.cursorShape;
}
@ -143,6 +149,10 @@ const reducer = (state = initial, action) => {
ret.borderColor = config.borderColor;
}
if (config.selectionColor) {
ret.selectionColor = config.selectionColor;
}
if (typeof config.padding !== 'undefined' && config.padding !== null) {
ret.padding = config.padding;
}