Просмотр исходного кода

Merge pull request #3553 from blink1073/xterm-3.0

Upgrade to xterm 3.0
Afshin Darian 7 лет назад
Родитель
Сommit
bded472611

+ 1 - 0
buildutils/src/update-dependency.ts

@@ -34,6 +34,7 @@ utils.getLernaPaths().forEach(pkgPath => {
   handlePackage(pkgPath);
 });
 handlePackage(path.resolve('.'));
+utils.run('yarn');
 
 
 /**

+ 1 - 1
dev_mode/package.json

@@ -93,7 +93,7 @@
     "react-dom": "~16.2.0",
     "sanitize-html": "~1.14.3",
     "url-parse": "~1.1.9",
-    "xterm": "~2.8.1"
+    "xterm": "~3.3.0"
   },
   "devDependencies": {
     "@jupyterlab/buildutils": "^0.7.2",

+ 1 - 1
jupyterlab/staging/package.json

@@ -93,7 +93,7 @@
     "react-dom": "~16.2.0",
     "sanitize-html": "~1.14.3",
     "url-parse": "~1.1.9",
-    "xterm": "~2.8.1"
+    "xterm": "~3.3.0"
   },
   "devDependencies": {
     "@jupyterlab/buildutils": "^0.7.2",

+ 4 - 0
jupyterlab/staging/yarn.lock

@@ -5314,6 +5314,10 @@ xterm@~2.8.1:
   version "2.8.1"
   resolved "https://registry.npmjs.org/xterm/-/xterm-2.8.1.tgz#3f6b939bcb8d015a1f247d66257102cb16a0b2e1"
 
+xterm@~3.3.0:
+  version "3.3.0"
+  resolved "https://registry.npmjs.org/xterm/-/xterm-3.3.0.tgz#b09a19fc2cd5decd21112e5c9dab0b61991f6cf3"
+
 y18n@^3.2.1:
   version "3.2.1"
   resolved "https://registry.npmjs.org/y18n/-/y18n-3.2.1.tgz#6d15fba884c08679c0d77e88e7759e811e07fa41"

+ 1 - 1
packages/metapackage/src/typings.d.ts

@@ -4,7 +4,7 @@
 /// <reference path="../../codemirror/typings/codemirror/codemirror.d.ts"/>
 /// <reference path="../../coreutils/typings/path-posix/path-posix.d.ts"/>
 /// <reference path="../../coreutils/typings/url-parse/url-parse.d.ts"/>
-/// <reference path="../../terminal/typings/xterm/xterm.d.ts"/>
+/// <reference path="../../terminal/src/xterm.d.ts"/>
 /// <reference path="../../vdom-extension/src/transform-vdom.d.ts"/>
 
 // TextEncoder interfaces for typedoc, since typedoc is still using TypeScript 2.7

+ 1 - 2
packages/terminal/package.json

@@ -33,10 +33,9 @@
     "@jupyterlab/apputils": "^0.16.3",
     "@jupyterlab/services": "^2.0.2",
     "@phosphor/coreutils": "^1.3.0",
-    "@phosphor/domutils": "^1.1.2",
     "@phosphor/messaging": "^1.2.2",
     "@phosphor/widgets": "^1.6.0",
-    "xterm": "~2.8.1"
+    "xterm": "~3.3.0"
   },
   "devDependencies": {
     "rimraf": "~2.6.2",

+ 0 - 4
packages/terminal/src/typings.d.ts

@@ -1,4 +0,0 @@
-// Copyright (c) Jupyter Development Team.
-// Distributed under the terms of the Modified BSD License.
-
-/// <reference path="../typings/xterm/xterm.d.ts"/>

+ 75 - 102
packages/terminal/src/widget.ts

@@ -5,10 +5,6 @@ import {
   TerminalSession
 } from '@jupyterlab/services';
 
-import {
-  ElementExt
-} from '@phosphor/domutils';
-
 import {
   Message, MessageLoop
 } from '@phosphor/messaging';
@@ -17,8 +13,13 @@ import {
   Widget
 } from '@phosphor/widgets';
 
-import * as Xterm
-  from 'xterm';
+import {
+  Terminal as Xterm, ITerminalOptions as IXtermOptions
+} from 'xterm';
+
+import {
+  fit
+} from 'xterm/lib/addons/fit';
 
 /**
  * The class name added to a terminal widget.
@@ -30,27 +31,6 @@ const TERMINAL_CLASS = 'jp-Terminal';
  */
 const TERMINAL_BODY_CLASS = 'jp-Terminal-body';
 
-/**
- * The class name add to the terminal widget when it has the dark theme.
- */
-const TERMINAL_DARK_THEME = 'jp-Terminal-dark';
-
-/**
- * The class name add to the terminal widget when it has the light theme.
- */
-const TERMINAL_LIGHT_THEME = 'jp-Terminal-light';
-
-
-/**
- * The number of rows to use in the dummy terminal.
- */
-const DUMMY_ROWS = 24;
-
-/**
- * The number of cols to use in the dummy terminal.
- */
-const DUMMY_COLS = 80;
-
 
 /**
  * A widget which manages a terminal session.
@@ -66,16 +46,15 @@ class Terminal extends Widget {
     super();
     this.addClass(TERMINAL_CLASS);
 
-    // Create the xterm, dummy terminal, and private style sheet.
+    // Create the xterm.
     this._term = new Xterm(Private.getConfig(options));
     this._initializeTerm();
-    this._dummyTerm = Private.createDummyTerm();
 
     // Initialize settings.
     let defaults = Terminal.defaultOptions;
-    this._fontSize = options.fontSize || defaults.fontSize;
     this._initialCommand = options.initialCommand || defaults.initialCommand;
     this.theme = options.theme || defaults.theme;
+
     this.id = `jp-Terminal-${Private.id++}`;
     this.title.label = 'Terminal';
   }
@@ -114,17 +93,17 @@ class Terminal extends Widget {
    * Get the font size of the terminal in pixels.
    */
   get fontSize(): number {
-    return this._fontSize;
+    return this._term.getOption('fontSize');
   }
 
   /**
    * Set the font size of the terminal in pixels.
    */
   set fontSize(size: number) {
-    if (this._fontSize === size) {
+    if (this.fontSize === size) {
       return;
     }
-    this._fontSize = size;
+    this._term.setOption('fontSize', size);
     this._needsResize = true;
     this.update();
   }
@@ -141,8 +120,14 @@ class Terminal extends Widget {
    */
   set theme(value: Terminal.Theme) {
     this._theme = value;
-    this.toggleClass(TERMINAL_LIGHT_THEME, value === 'light');
-    this.toggleClass(TERMINAL_DARK_THEME, value === 'dark');
+    if (value === 'light') {
+      this.addClass('jp-mod-light');
+      this._term.setOption('theme', Private.lightTheme);
+    } else {
+      this.removeClass('jp-mod-light');
+      this._term.setOption('theme', Private.darkTheme);
+    }
+    this.update();
   }
 
   /**
@@ -150,7 +135,6 @@ class Terminal extends Widget {
    */
   dispose(): void {
     this._session = null;
-    this._box = null;
     super.dispose();
   }
 
@@ -204,7 +188,7 @@ class Terminal extends Widget {
    */
   protected onResize(msg: Widget.ResizeMessage): void {
     this._offsetWidth = msg.width;
-    this._offsetHeight = msg.height;
+    this._offetHeight = msg.height;
     this._needsResize = true;
     this.update();
   }
@@ -213,12 +197,18 @@ class Terminal extends Widget {
    * A message handler invoked on an `'update-request'` message.
    */
   protected onUpdateRequest(msg: Message): void {
-    if (!this.isVisible) {
+    if (!this.isVisible || !this.isAttached) {
       return;
     }
 
+    // Open the terminal if necessary.
+    if (!this._termOpened) {
+      this._term.open(this.node);
+      this._term.element.classList.add(TERMINAL_BODY_CLASS);
+      this._termOpened = true;
+    }
+
     if (this._needsResize) {
-      this._snapTermSizing();
       this._resizeTerminal();
     }
   }
@@ -239,12 +229,9 @@ class Terminal extends Widget {
   }
 
   /**
-   * Create the terminal object.
+   * Initialize the terminal object.
    */
   private _initializeTerm(): void {
-    this._term.open(this.node, false);
-    this._term.element.classList.add(TERMINAL_BODY_CLASS);
-
     this._term.on('data', (data: string) => {
       if (this._session) {
         this._session.send({
@@ -277,64 +264,41 @@ class Terminal extends Widget {
     }
   }
 
-  /**
-   * Use the dummy terminal to measure the row and column sizes.
-   */
-  private _snapTermSizing(): void {
-    const node = this._dummyTerm;
-
-    this._term.element.style.fontSize = `${this.fontSize}px`;
-    this._term.element.appendChild(node);
-    this._rowHeight = node.offsetHeight / DUMMY_ROWS;
-    this._colWidth = node.offsetWidth / DUMMY_COLS;
-    this._term.element.removeChild(node);
-  }
-
   /**
    * Resize the terminal based on computed geometry.
    */
   private _resizeTerminal() {
-    const { node } = this;
-    const offsetWidth = this._offsetWidth < 0 ? node.offsetWidth
-      : this._offsetWidth;
-    const offsetHeight = this._offsetHeight < 0 ? node.offsetHeight
-      : this._offsetHeight;
-    const box = this._box = ElementExt.boxSizing(this.node);
-    const height = offsetHeight - box.verticalSum;
-    const width = offsetWidth - box.horizontalSum;
-    const rows = Math.floor(height / this._rowHeight) - 1;
-    const cols = Math.floor(width / this._colWidth) - 1;
-
-    this._term.resize(cols, rows);
-    this._sessionSize = [rows, cols, height, width];
+    fit(this._term);
+    if (this._offsetWidth === -1) {
+      this._offsetWidth = this.node.offsetWidth;
+    }
+    if (this._offetHeight === -1) {
+      this._offetHeight = this.node.offsetHeight;
+    }
     this._setSessionSize();
     this._needsResize = false;
   }
 
   /**
-   * Send the size to the session.
+   * Set the size of the terminal in the session.
    */
   private _setSessionSize(): void {
-    const session = this._session;
-
-    if (session) {
-      session.send({ type: 'set_size', content: this._sessionSize });
+    let content = [
+      this._term.rows, this._term.cols, this._offetHeight, this._offsetWidth
+    ];
+    if (this._session) {
+      this._session.send({ type: 'set_size', content });
     }
   }
 
   private _term: Xterm;
-  private _dummyTerm: HTMLElement;
-  private _fontSize = -1;
   private _needsResize = true;
-  private _rowHeight = -1;
-  private _colWidth = -1;
-  private _offsetWidth = -1;
-  private _offsetHeight = -1;
-  private _sessionSize: [number, number, number, number] = [1, 1, 1, 1];
   private _theme: Terminal.Theme = 'dark';
-  private _box: ElementExt.IBoxSizing | null = null;
   private _session: TerminalSession.ISession | null = null;
   private _initialCommand: string;
+  private _termOpened = false;
+  private _offsetWidth = -1;
+  private _offetHeight = -1;
 }
 
 
@@ -396,39 +360,48 @@ namespace Private {
    * Get term.js options from ITerminalOptions.
    */
   export
-  function getConfig(options: Partial<Terminal.IOptions>): Xterm.IOptions {
-    let config: Xterm.IOptions = {};
+  function getConfig(options: Partial<Terminal.IOptions>): IXtermOptions {
+    let config: IXtermOptions = {};
     if (options.cursorBlink !== void 0) {
       config.cursorBlink = options.cursorBlink;
     } else {
       config.cursorBlink = Terminal.defaultOptions.cursorBlink;
     }
+    if (options.fontSize !== void 0) {
+      config.fontSize = options.fontSize;
+    } else {
+      config.fontSize = Terminal.defaultOptions.fontSize;
+    }
     return config;
   }
 
   /**
-   * Create a dummy terminal element used to measure text size.
+   * An incrementing counter for ids.
    */
   export
-  function createDummyTerm(): HTMLElement {
-    let node = document.createElement('div');
-    let rowspan = document.createElement('span');
-    rowspan.innerHTML = Array(DUMMY_ROWS).join('a<br>');
-    let colspan = document.createElement('span');
-    colspan.textContent = Array(DUMMY_COLS + 1).join('a');
-    node.appendChild(rowspan);
-    node.appendChild(colspan);
-    node.style.visibility = 'hidden';
-    node.style.position = 'absolute';
-    node.style.height = 'auto';
-    node.style.width = 'auto';
-    (node.style as any)['white-space'] = 'nowrap';
-    return node;
-  }
+  let id = 0;
 
   /**
-   * An incrementing counter for ids.
+   * The light terminal theme.
    */
   export
-  let id = 0;
+  const lightTheme = {
+    foreground: '#000',
+    background: '#fff',
+    cursor: '#616161',  // md-grey-700
+    cursorAccent: '#F5F5F5',  // md-grey-100
+    selection: 'rgba(97, 97, 97, 0.3)',  // md-grey-700
+  };
+
+  /**
+   * The dark terminal theme.
+   */
+  export
+  const darkTheme = {
+    foreground: '#fff',
+    background: '#000',
+    cursor: '#fff',
+    cursorAccent: '#000',
+    selection: 'rgba(255, 255, 255, 0.3)',
+  };
 }

+ 6 - 0
packages/terminal/src/xterm.d.ts

@@ -0,0 +1,6 @@
+
+// Declaration for the xterm fit addon.
+
+declare module 'xterm/lib/addons/fit' {
+    export function fit(term: any): void;
+}

+ 3 - 79
packages/terminal/style/index.css

@@ -6,95 +6,19 @@
 @import url('~xterm/src/xterm.css');
 
 
-:root {
-  --jp-private-terminal-lightCursor-color: var(--md-grey-700);
-}
-
-
 .jp-Terminal {
   min-width: 240px;
   min-height: 120px;
-  padding: 8px;
-  margin: 0;
-}
-
-
-.jp-Terminal-body {
-  font-family: var(--jp-code-font-family);
-  outline: none;
-  user-select: text;
-  -webkit-user-select: text;
-}
-
-
-.terminal {
-  font-family: var(--jp-code-font-family);
-}
-
-
-.terminal .xterm-viewport {
-  overflow-y: auto;
-}
-
-
-.jp-Terminal-dark {
   background: black;
-  color: white;
 }
 
 
-/**
- * xterm.js ships with the dark theme. Here we add the somewhat subtle styles to get a light theme.
- * This approach is taken directly from the xterm.js css and modified for our light theme.
- */
-
-.jp-Terminal-light {
+.jp-Terminal.jp-mod-light {
   background: white;
-  color: black;
 }
 
 
-.jp-Terminal-light .terminal {
-  background: white;
-  color: black;
-}
-
-
-.jp-Terminal-light .terminal.focus:not(.xterm-cursor-style-underline):not(.xterm-cursor-style-bar) .terminal-cursor {
-    background-color: var(--jp-private-terminal-lightCursor-color);
-    color: white;
-}
-
-
-.jp-Terminal-light .terminal:not(.focus) .terminal-cursor {
-    outline: 1px solid var(--jp-private-terminal-lightCursor-color);
-}
-
-
-.jp-Terminal-light .terminal:not(.xterm-cursor-style-underline):not(.xterm-cursor-style-bar).focus.xterm-cursor-blink-on .terminal-cursor {
-    background-color: transparent;
-    color: inherit;
-}
-
-
-.jp-Terminal-light .terminal.xterm-cursor-style-bar .terminal-cursor::before,
-.jp-Terminal-light .terminal.xterm-cursor-style-underline .terminal-cursor::before {
-    background-color: var(--jp-private-terminal-lightCursor-color);
-}
-
-
-.jp-Terminal-light .terminal.xterm-cursor-style-bar.focus.xterm-cursor-blink .terminal-cursor::before,
-.jp-Terminal-light .terminal.xterm-cursor-style-underline.focus.xterm-cursor-blink .terminal-cursor::before {
-    background-color: var(--jp-private-terminal-lightCursor-color);
-}
-
-
-.jp-Terminal-light .terminal .composition-view {
-    background: white;
-    color: black;
+.jp-Terminal-body {
+  padding: 8px;
 }
 
-
-.jp-Terminal-light .terminal .xterm-viewport {
-    background-color: white;
-}

+ 0 - 71
packages/terminal/typings/xterm/xterm.d.ts

@@ -1,71 +0,0 @@
-// Type definitions for xterm.js 2.6.0
-// Project: https://github.com/sourcelair/xterm.js/
-// Definitions by: Steven Silvester <https://github.com/blink1073>
-// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped
-
-
-declare class Terminal {
-
-  constructor(options?: Terminal.IOptions);
-
-  options: Terminal.IOptions;
-
-  element: HTMLElement;
-
-  textarea: HTMLElement;
-
-  attachCustomKeydownHandler(callback: (event: KeyboardEvent) => boolean): void;
-
-  blur(): void;
-
-  clear(): void;
-
-  destroy(): void;
-
-  focus(): void;
-
-  getOption(key: string): number | boolean;
-  getOption(key: 'rows'): number;
-  getOption(key: 'cols'): number;
-  getOption(key: 'cursorBlink'): boolean;
-
-  on(event: string, callback: (...args: any[]) => void): void;
-
-  off(event: string, callback: (...args: any[]) => void): void;
-
-  open(parent: HTMLElement, focus?: boolean): void;
-
-  refresh(start: number, end: number, queue?: boolean): void;
-
-  reset(): void;
-
-  resize(x: number, y: number): void;
-
-  scrollDisp(n: number): void;
-
-  setOption(key: string, value: number | boolean): void;
-  setOption(key: 'rows', value: number): void;
-  setOption(key: 'cols', value: number): void;
-  setOption(key: 'cursorBlink', value: boolean): void;
-
-  write(text: string): void;
-
-  writeln(text: string): void;
-}
-
-
-declare namespace Terminal {
-  export
-  interface IOptions {
-    cursorBlink?: boolean;
-
-    rows?: number;
-
-    cols?: number;
-  }
-}
-
-
-declare module 'xterm' {
-  export = Terminal;
-}

+ 0 - 1
tests/test-terminal/package.json

@@ -17,7 +17,6 @@
   "dependencies": {
     "@jupyterlab/services": "^2.0.2",
     "@jupyterlab/terminal": "^0.16.2",
-    "@phosphor/domutils": "^1.1.2",
     "@phosphor/messaging": "^1.2.2",
     "@phosphor/widgets": "^1.6.0",
     "expect.js": "~0.3.1"

+ 11 - 16
tests/test-terminal/src/terminal.spec.ts

@@ -7,10 +7,6 @@ import {
   TerminalSession
 } from '@jupyterlab/services';
 
-import {
-  Platform
-} from '@phosphor/domutils';
-
 import {
   Message, MessageLoop
 } from '@phosphor/messaging';
@@ -74,8 +70,12 @@ describe('terminal/index', () => {
       }).then(done, done);
     });
 
-    beforeEach(() => {
+    beforeEach((done) => {
       widget = new LogTerminal();
+      Widget.attach(widget, document.body);
+      requestAnimationFrame(() => {
+        done();
+      });
     });
 
     afterEach(() => {
@@ -125,21 +125,13 @@ describe('terminal/index', () => {
 
     describe('#theme', () => {
 
-      it('should be dark by default', (done) => {
+      it('should be dark by default', () => {
         expect(widget.theme).to.be('dark');
-        requestAnimationFrame(() => {
-          expect(widget.hasClass('jp-Terminal-dark')).to.be(true);
-          done();
-        });
       });
 
-      it('should be light if we change it', (done) => {
+      it('should be light if we change it', () => {
         widget.theme = 'light';
         expect(widget.theme).to.be('light');
-        requestAnimationFrame(() => {
-          expect(widget.hasClass('jp-Terminal-light')).to.be(true);
-          done();
-        });
       });
 
     });
@@ -178,6 +170,7 @@ describe('terminal/index', () => {
 
       it('should post an update request', (done) => {
         widget.session = session;
+        Widget.detach(widget);
         Widget.attach(widget, document.body);
         requestAnimationFrame(() => {
           expect(widget.methods).to.contain('onUpdateRequest');
@@ -192,6 +185,7 @@ describe('terminal/index', () => {
       it('should post an update request', (done) => {
         widget.session = session;
         widget.hide();
+        Widget.detach(widget);
         Widget.attach(widget, document.body);
         requestAnimationFrame(() => {
           widget.methods = [];
@@ -222,12 +216,12 @@ describe('terminal/index', () => {
     describe('#onUpdateRequest()', () => {
 
       it('should set the style of the terminal', () => {
+        Widget.detach(widget);
         Widget.attach(widget, document.body);
         MessageLoop.sendMessage(widget, Widget.Msg.UpdateRequest);
         expect(widget.methods).to.contain('onUpdateRequest');
         let style = window.getComputedStyle(widget.node);
         expect(style.backgroundColor).to.be('rgb(0, 0, 0)');
-        expect(style.color).to.be('rgb(255, 255, 255)');
       });
 
     });
@@ -244,6 +238,7 @@ describe('terminal/index', () => {
     describe('#onActivateRequest', () => {
 
       it('should focus the terminal element', () => {
+        Widget.detach(widget);
         Widget.attach(widget, document.body);
         expect(widget.node.contains(document.activeElement)).to.be(false);
         MessageLoop.sendMessage(widget, Widget.Msg.ActivateRequest);

+ 3 - 3
yarn.lock

@@ -8755,9 +8755,9 @@ xtend@^4.0.0, xtend@^4.0.1, xtend@~4.0.1:
   version "4.0.1"
   resolved "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz#a5c6d532be656e23db820efb943a1f04998d63af"
 
-xterm@~2.8.1:
-  version "2.8.1"
-  resolved "https://registry.npmjs.org/xterm/-/xterm-2.8.1.tgz#3f6b939bcb8d015a1f247d66257102cb16a0b2e1"
+xterm@~3.3.0:
+  version "3.3.0"
+  resolved "https://registry.npmjs.org/xterm/-/xterm-3.3.0.tgz#b09a19fc2cd5decd21112e5c9dab0b61991f6cf3"
 
 y18n@^3.2.1:
   version "3.2.1"