diff --git a/package.json b/package.json index 44cc2e88..5c7574ee 100644 --- a/package.json +++ b/package.json @@ -8,6 +8,7 @@ "license": "MIT", "devDependencies": { "@types/chai": "^3.4.34", + "@types/glob": "^5.0.35", "@types/jsdom": "^11.0.1", "@types/mocha": "^2.2.33", "@types/node": "6.0.108", diff --git a/src/CompositionHelper.test.ts b/src/CompositionHelper.test.ts index 02231723..053a0c29 100644 --- a/src/CompositionHelper.test.ts +++ b/src/CompositionHelper.test.ts @@ -5,13 +5,14 @@ import { assert } from 'chai'; import { CompositionHelper } from './CompositionHelper'; +import { ITerminal } from './Types'; describe('CompositionHelper', () => { - let terminal; - let compositionHelper; - let compositionView; - let textarea; - let handledText; + let terminal: ITerminal; + let compositionHelper: CompositionHelper; + let compositionView: HTMLElement; + let textarea: HTMLTextAreaElement; + let handledText: string; beforeEach(() => { compositionView = { @@ -27,14 +28,14 @@ describe('CompositionHelper', () => { top: 0 }, textContent: '' - }; + } as any; textarea = { value: '', style: { left: 0, top: 0 } - }; + } as any; terminal = { element: { querySelector: () => { @@ -54,7 +55,7 @@ describe('CompositionHelper', () => { options: { lineHeight: 1 } - }; + } as any; handledText = ''; compositionHelper = new CompositionHelper(textarea, compositionView, terminal); }); @@ -63,7 +64,7 @@ describe('CompositionHelper', () => { it('Should insert simple characters', (done) => { // First character 'ㅇ' compositionHelper.compositionstart(); - compositionHelper.compositionupdate({ data: 'ㅇ' }); + compositionHelper.compositionupdate({ data: 'ㅇ' }); textarea.value = 'ㅇ'; setTimeout(() => { // wait for any textarea updates compositionHelper.compositionend(); @@ -71,7 +72,7 @@ describe('CompositionHelper', () => { assert.equal(handledText, 'ㅇ'); // Second character 'ㅇ' compositionHelper.compositionstart(); - compositionHelper.compositionupdate({ data: 'ㅇ' }); + compositionHelper.compositionupdate({ data: 'ㅇ' }); textarea.value = 'ㅇㅇ'; setTimeout(() => { // wait for any textarea updates compositionHelper.compositionend(); @@ -87,13 +88,13 @@ describe('CompositionHelper', () => { it('Should insert complex characters', (done) => { // First character '앙' compositionHelper.compositionstart(); - compositionHelper.compositionupdate({ data: 'ㅇ' }); + compositionHelper.compositionupdate({ data: 'ㅇ' }); textarea.value = 'ㅇ'; setTimeout(() => { // wait for any textarea updates - compositionHelper.compositionupdate({ data: '아' }); + compositionHelper.compositionupdate({ data: '아' }); textarea.value = '아'; setTimeout(() => { // wait for any textarea updates - compositionHelper.compositionupdate({ data: '앙' }); + compositionHelper.compositionupdate({ data: '앙' }); textarea.value = '앙'; setTimeout(() => { // wait for any textarea updates compositionHelper.compositionend(); @@ -101,13 +102,13 @@ describe('CompositionHelper', () => { assert.equal(handledText, '앙'); // Second character '앙' compositionHelper.compositionstart(); - compositionHelper.compositionupdate({ data: 'ㅇ' }); + compositionHelper.compositionupdate({ data: 'ㅇ' }); textarea.value = '앙ㅇ'; setTimeout(() => { // wait for any textarea updates - compositionHelper.compositionupdate({ data: '아' }); + compositionHelper.compositionupdate({ data: '아' }); textarea.value = '앙아'; setTimeout(() => { // wait for any textarea updates - compositionHelper.compositionupdate({ data: '앙' }); + compositionHelper.compositionupdate({ data: '앙' }); textarea.value = '앙앙'; setTimeout(() => { // wait for any textarea updates compositionHelper.compositionend(); @@ -127,19 +128,19 @@ describe('CompositionHelper', () => { it('Should insert complex characters that change with following character', (done) => { // First character '아' compositionHelper.compositionstart(); - compositionHelper.compositionupdate({ data: 'ㅇ' }); + compositionHelper.compositionupdate({ data: 'ㅇ' }); textarea.value = 'ㅇ'; setTimeout(() => { // wait for any textarea updates - compositionHelper.compositionupdate({ data: '아' }); + compositionHelper.compositionupdate({ data: '아' }); textarea.value = '아'; setTimeout(() => { // wait for any textarea updates // Start second character '아' in first character - compositionHelper.compositionupdate({ data: '앙' }); + compositionHelper.compositionupdate({ data: '앙' }); textarea.value = '앙'; setTimeout(() => { // wait for any textarea updates compositionHelper.compositionend(); compositionHelper.compositionstart(); - compositionHelper.compositionupdate({ data: '아' }); + compositionHelper.compositionupdate({ data: '아' }); textarea.value = '아아'; setTimeout(() => { // wait for any textarea updates compositionHelper.compositionend(); @@ -156,14 +157,14 @@ describe('CompositionHelper', () => { it('Should insert multi-characters compositions', (done) => { // First character 'だ' compositionHelper.compositionstart(); - compositionHelper.compositionupdate({ data: 'd' }); + compositionHelper.compositionupdate({ data: 'd' }); textarea.value = 'd'; setTimeout(() => { // wait for any textarea updates - compositionHelper.compositionupdate({ data: 'だ' }); + compositionHelper.compositionupdate({ data: 'だ' }); textarea.value = 'だ'; setTimeout(() => { // wait for any textarea updates // Second character 'あ' - compositionHelper.compositionupdate({ data: 'だあ' }); + compositionHelper.compositionupdate({ data: 'だあ' }); textarea.value = 'だあ'; setTimeout(() => { // wait for any textarea updates compositionHelper.compositionend(); @@ -179,18 +180,18 @@ describe('CompositionHelper', () => { it('Should insert multi-character compositions that are converted to other characters with the same length', (done) => { // First character 'だ' compositionHelper.compositionstart(); - compositionHelper.compositionupdate({ data: 'd' }); + compositionHelper.compositionupdate({ data: 'd' }); textarea.value = 'd'; setTimeout(() => { // wait for any textarea updates - compositionHelper.compositionupdate({ data: 'だ' }); + compositionHelper.compositionupdate({ data: 'だ' }); textarea.value = 'だ'; setTimeout(() => { // wait for any textarea updates // Second character 'ー' - compositionHelper.compositionupdate({ data: 'だー' }); + compositionHelper.compositionupdate({ data: 'だー' }); textarea.value = 'だー'; setTimeout(() => { // wait for any textarea updates // Convert to katakana 'ダー' - compositionHelper.compositionupdate({ data: 'ダー' }); + compositionHelper.compositionupdate({ data: 'ダー' }); textarea.value = 'ダー'; setTimeout(() => { // wait for any textarea updates compositionHelper.compositionend(); @@ -207,18 +208,18 @@ describe('CompositionHelper', () => { it('Should insert multi-character compositions that are converted to other characters with different lengths', (done) => { // First character 'い' compositionHelper.compositionstart(); - compositionHelper.compositionupdate({ data: 'い' }); + compositionHelper.compositionupdate({ data: 'い' }); textarea.value = 'い'; setTimeout(() => { // wait for any textarea updates // Second character 'ま' - compositionHelper.compositionupdate({ data: 'いm' }); + compositionHelper.compositionupdate({ data: 'いm' }); textarea.value = 'いm'; setTimeout(() => { // wait for any textarea updates - compositionHelper.compositionupdate({ data: 'いま' }); + compositionHelper.compositionupdate({ data: 'いま' }); textarea.value = 'いま'; setTimeout(() => { // wait for any textarea updates // Convert to kanji '今' - compositionHelper.compositionupdate({ data: '今' }); + compositionHelper.compositionupdate({ data: '今' }); textarea.value = '今'; setTimeout(() => { // wait for any textarea updates compositionHelper.compositionend(); @@ -235,7 +236,7 @@ describe('CompositionHelper', () => { it('Should insert non-composition characters input immediately after composition characters', (done) => { // First character 'ㅇ' compositionHelper.compositionstart(); - compositionHelper.compositionupdate({ data: 'ㅇ' }); + compositionHelper.compositionupdate({ data: 'ㅇ' }); textarea.value = 'ㅇ'; setTimeout(() => { // wait for any textarea updates compositionHelper.compositionend(); diff --git a/src/EscapeSequenceParser.test.ts b/src/EscapeSequenceParser.test.ts index d9ea64ce..dffbccbc 100644 --- a/src/EscapeSequenceParser.test.ts +++ b/src/EscapeSequenceParser.test.ts @@ -237,7 +237,7 @@ describe('EscapeSequenceParser', function (): void { '\x89', '\x8a', '\x8b', '\x8c', '\x8d', '\x8e', '\x8f', '\x91', '\x92', '\x93', '\x94', '\x95', '\x96', '\x97', '\x99', '\x9a' ]; - const exceptions = { + const exceptions: { [key: number]: { [key: string]: any[] } } = { 8: { '\x18': [], '\x1a': [] } // simply abort osc state }; parser.reset(); @@ -247,7 +247,7 @@ describe('EscapeSequenceParser', function (): void { parser.currentState = state; parser.parse(exes[i]); chai.expect(parser.currentState).equal(ParserState.GROUND); - testTerminal.compare(((exceptions[state]) ? exceptions[state][exes[i]] : 0) || [['exe', exes[i]]]); + testTerminal.compare((state in exceptions ? exceptions[state][exes[i]] : 0) || [['exe', exes[i]]]); parser.reset(); testTerminal.clear(); } @@ -1099,20 +1099,20 @@ describe('EscapeSequenceParser', function (): void { describe('set/clear handler', function (): void { const INPUT = '\x1b[1;31mhello \x1b%Gwor\x1bEld!\x1b[0m\r\n$>\x1b]1;foo=bar\x1b\\'; - let parser2 = null; + let parser2: TestEscapeSequenceParser = null; let print = ''; - let esc = []; - let csi = []; - let exe = []; - let osc = []; - let dcs = []; + const esc: string[] = []; + const csi: [string, number[], string][] = []; + const exe: string[] = []; + const osc: [number, string][] = []; + const dcs: ([string] | [string, string] | [string, string, number[], number])[] = []; function clearAccu(): void { print = ''; - esc = []; - csi = []; - exe = []; - osc = []; - dcs = []; + esc.length = 0; + csi.length = 0; + exe.length = 0; + osc.length = 0; + dcs.length = 0; } beforeEach(function (): void { parser2 = new TestEscapeSequenceParser(); diff --git a/src/InputHandler.ts b/src/InputHandler.ts index f5db133f..16fedea2 100644 --- a/src/InputHandler.ts +++ b/src/InputHandler.ts @@ -15,7 +15,7 @@ import { EscapeSequenceParser } from './EscapeSequenceParser'; /** * Map collect to glevel. Used in `selectCharset`. */ -const GLEVEL = {'(': 0, ')': 1, '*': 2, '+': 3, '-': 1, '.': 2}; +const GLEVEL: {[key: string]: number} = {'(': 0, ')': 1, '*': 2, '+': 3, '-': 1, '.': 2}; /** @@ -77,7 +77,7 @@ class DECRQSS implements IDcsHandler { // TODO: report real settings instead of 0m return this._terminal.send(`${C0.ESC}P1$r0m${C0.ESC}\\`); case ' q': // DECSCUSR - const STYLES = {'block': 2, 'underline': 4, 'bar': 6}; + const STYLES: {[key: string]: number} = {'block': 2, 'underline': 4, 'bar': 6}; let style = STYLES[this._terminal.getOption('cursorStyle')]; style -= this._terminal.getOption('cursorBlink'); return this._terminal.send(`${C0.ESC}P1$r${style} q${C0.ESC}\\`); diff --git a/src/Terminal.integration.ts b/src/Terminal.integration.ts index 54e76518..bb50d2d0 100644 --- a/src/Terminal.integration.ts +++ b/src/Terminal.integration.ts @@ -14,6 +14,11 @@ import * as pty from 'node-pty'; import { assert } from 'chai'; import { Terminal } from './Terminal'; import { CHAR_DATA_CHAR_INDEX } from './Buffer'; +import { IViewport } from './Types'; + +class TestTerminal extends Terminal { + innerWrite(): void { this._innerWrite(); } +} let primitivePty: any; @@ -87,12 +92,12 @@ if (os.platform() !== 'win32') { /** tests */ describe('xterm output comparison', () => { - let xterm; + let xterm: TestTerminal; beforeEach(() => { - xterm = new Terminal({ cols: COLS, rows: ROWS }); + xterm = new TestTerminal({ cols: COLS, rows: ROWS }); xterm.refresh = () => {}; - xterm.viewport = { + xterm.viewport = { syncScrollArea: () => {} }; }); @@ -125,7 +130,7 @@ if (os.platform() !== 'win32') { // Perform a synchronous .write(data) xterm.writeBuffer.push(fromPty); - xterm._innerWrite(); + xterm.innerWrite(); const fromEmulator = terminalToString(xterm); console.log = CONSOLE_LOG; diff --git a/src/Terminal.test.ts b/src/Terminal.test.ts index 5b7039e9..f6cbad52 100644 --- a/src/Terminal.test.ts +++ b/src/Terminal.test.ts @@ -176,7 +176,7 @@ describe('term.js addons', () => { describe('scroll', () => { describe('scrollLines', () => { - let startYDisp; + let startYDisp: number; beforeEach(() => { for (let i = 0; i < term.rows * 2; i++) { term.writeln('test'); @@ -211,7 +211,7 @@ describe('term.js addons', () => { }); describe('scrollPages', () => { - let startYDisp; + let startYDisp: number; beforeEach(() => { for (let i = 0; i < term.rows * 3; i++) { term.writeln('test'); @@ -248,7 +248,7 @@ describe('term.js addons', () => { }); describe('scrollToBottom', () => { - let startYDisp; + let startYDisp: number; beforeEach(() => { for (let i = 0; i < term.rows * 3; i++) { term.writeln('test'); @@ -269,7 +269,7 @@ describe('term.js addons', () => { }); describe('scrollToLine', () => { - let startYDisp; + let startYDisp: number; beforeEach(() => { for (let i = 0; i < term.rows * 3; i++) { term.writeln('test'); @@ -712,8 +712,8 @@ describe('term.js addons', () => { }); describe('Third level shift', () => { - let evKeyDown; - let evKeyPress; + let evKeyDown: any; + let evKeyPress: any; beforeEach(() => { term.handler = () => {}; diff --git a/src/Terminal.ts b/src/Terminal.ts index 63f7efdb..92ab39e1 100644 --- a/src/Terminal.ts +++ b/src/Terminal.ts @@ -51,7 +51,7 @@ import { ITheme, ILocalizableStrings, IMarker, IDisposable } from 'xterm'; import { removeTerminalFromCache } from './renderer/atlas/CharAtlasCache'; // reg + shift key mappings for digits and special chars -const KEYCODE_KEY_MAPPINGS = { +const KEYCODE_KEY_MAPPINGS: { [key: number]: [string, string]} = { // digits 0-9 48: ['0', ')'], 49: ['1', '!'], @@ -277,8 +277,6 @@ export class Terminal extends EventEmitter implements ITerminal, IDisposable, II if (this.options[key] == null) { this.options[key] = DEFAULT_OPTIONS[key]; } - // TODO: We should move away from duplicate options on the Terminal object - this[key] = this.options[key]; }); // this.context = options.context || window; @@ -384,11 +382,7 @@ export class Terminal extends EventEmitter implements ITerminal, IDisposable, II throw new Error('No option with key "' + key + '"'); } - if (typeof this.options[key] !== 'undefined') { - return this.options[key]; - } - - return this[key]; + return this.options[key]; } /** @@ -462,7 +456,6 @@ export class Terminal extends EventEmitter implements ITerminal, IDisposable, II } break; } - this[key] = value; this.options[key] = value; switch (key) { case 'fontFamily': @@ -561,7 +554,7 @@ export class Terminal extends EventEmitter implements ITerminal, IDisposable, II } copyHandler(event, this, this.selectionManager); }); - const pasteHandlerWrapper = event => pasteHandler(event, this); + const pasteHandlerWrapper = (event: ClipboardEvent) => pasteHandler(event, this); on(this.textarea, 'paste', pasteHandlerWrapper); on(this.element, 'paste', pasteHandlerWrapper); @@ -1292,7 +1285,7 @@ export class Terminal extends EventEmitter implements ITerminal, IDisposable, II } } - private _innerWrite(): void { + protected _innerWrite(): void { const writeBatch = this.writeBuffer.splice(0, WRITE_BATCH_SIZE); while (writeBatch.length > 0) { const data = writeBatch.shift(); diff --git a/src/Types.ts b/src/Types.ts index 5075daa9..97a6f9e4 100644 --- a/src/Types.ts +++ b/src/Types.ts @@ -261,6 +261,7 @@ export interface ICharMeasure { // TODO: The options that are not in the public API should be reviewed export interface ITerminalOptions extends IPublicTerminalOptions { + [key: string]: any; cancelEvents?: boolean; convertEol?: boolean; debug?: boolean; diff --git a/src/addons/attach/attach.ts b/src/addons/attach/attach.ts index d0e8bd4b..e6d92b56 100644 --- a/src/addons/attach/attach.ts +++ b/src/addons/attach/attach.ts @@ -36,10 +36,11 @@ export function attach(term: Terminal, socket: WebSocket, bidirectional: boolean } }; - let myTextDecoder; + // TODO: This should be typed but there seem to be issues importing the type + let myTextDecoder: any; addonTerminal.__getMessage = function(ev: MessageEvent): void { - let str; + let str: string; if (typeof ev.data === 'object') { if (!myTextDecoder) { diff --git a/src/renderer/ColorManager.test.ts b/src/renderer/ColorManager.test.ts index 0012c1ed..26fb2895 100644 --- a/src/renderer/ColorManager.test.ts +++ b/src/renderer/ColorManager.test.ts @@ -36,7 +36,7 @@ describe('ColorManager', () => { for (const key of Object.keys(cm.colors)) { if (key !== 'ansi') { // A #rrggbb or rgba(...) - assert.ok(cm.colors[key].css.length >= 7); + assert.ok((cm.colors)[key].css.length >= 7); } } assert.equal(cm.colors.ansi.length, 256); diff --git a/src/renderer/atlas/CharAtlasUtils.ts b/src/renderer/atlas/CharAtlasUtils.ts index 39284d32..59ac07df 100644 --- a/src/renderer/atlas/CharAtlasUtils.ts +++ b/src/renderer/atlas/CharAtlasUtils.ts @@ -9,7 +9,7 @@ import { ICharAtlasConfig } from '../../shared/atlas/Types'; export function generateConfig(scaledCharWidth: number, scaledCharHeight: number, terminal: ITerminal, colors: IColorSet): ICharAtlasConfig { // null out some fields that don't matter - const clonedColors = { + const clonedColors = { foreground: colors.foreground, background: colors.background, cursor: null, diff --git a/src/renderer/atlas/LRUMap.ts b/src/renderer/atlas/LRUMap.ts index 4a03f2aa..eccfbfea 100644 --- a/src/renderer/atlas/LRUMap.ts +++ b/src/renderer/atlas/LRUMap.ts @@ -11,7 +11,7 @@ interface ILinkedListNode { } export default class LRUMap { - private _map = {}; + private _map: { [key: string]: ILinkedListNode } = {}; private _head: ILinkedListNode = null; private _tail: ILinkedListNode = null; private _nodePool: ILinkedListNode[] = []; diff --git a/src/shared/atlas/CharAtlasGenerator.ts b/src/shared/atlas/CharAtlasGenerator.ts index e0cfbcf7..f78a6d41 100644 --- a/src/shared/atlas/CharAtlasGenerator.ts +++ b/src/shared/atlas/CharAtlasGenerator.ts @@ -106,7 +106,7 @@ export function generateStaticCharAtlasTexture(context: Window, canvasFactory: ( return canvas; } // Transfer to an ImageBitmap is this is an OffscreenCanvas - return new Promise(r => r(canvas.transferToImageBitmap())); + return new Promise((r: (bitmap: ImageBitmap) => void) => r(canvas.transferToImageBitmap())); } const charAtlasImageData = ctx.getImageData(0, 0, canvas.width, canvas.height); diff --git a/src/utils/Clone.test.ts b/src/utils/Clone.test.ts index f5708a9e..b24452c8 100644 --- a/src/utils/Clone.test.ts +++ b/src/utils/Clone.test.ts @@ -39,7 +39,7 @@ describe('clone', () => { }); it('should clone null values', () => { - const test = { + const test: any = { a: null }; diff --git a/src/utils/TestUtils.test.ts b/src/utils/TestUtils.test.ts index e3ae89d3..42c4d974 100644 --- a/src/utils/TestUtils.test.ts +++ b/src/utils/TestUtils.test.ts @@ -267,7 +267,7 @@ export class MockInputHandlingTerminal implements IInputHandlingTerminal { throw new Error('Method not implemented.'); } setOption(key: string, value: any): void { - this.options[key] = value; + (this.options)[key] = value; } on(type: string, listener: XtermListener): void { throw new Error('Method not implemented.'); diff --git a/tsconfig.json b/tsconfig.json index 2e7404e2..c33793f5 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -12,7 +12,8 @@ "outDir": "lib", "sourceMap": true, "removeComments": true, - "noUnusedLocals": true + "noUnusedLocals": true, + "noImplicitAny": true }, "include": [ "src/**/*",