Support bracketed paste mode
- As described in https://cirw.in/blog/bracketed-paste - Example support in vim: - https://github.com/ConradIrwin/vim-bracketed-paste/blob/master/plugin/bracketed-paste.vim - http://vimdoc.sourceforge.net/htmldoc/term.html#raw-terminal-mode - Required for e.g. ipython, via python-prompt-toolkit: - https://github.com/ipython/ipython - https://github.com/jonathanslenders/python-prompt-toolkit Repro: - Run `ipython` (version 6.2.1) - Copy multiple lines of text and paste into ipython prompt, e.g. ```py print(1) print(2) print(3) ``` - Before: first line would paste and run, and subsequent lines are lost - After: all lines paste into single prompt
This commit is contained in:
@@ -66,4 +66,18 @@ describe('InputHandler', () => {
|
|||||||
assert.equal(terminal.options['cursorBlink'], false);
|
assert.equal(terminal.options['cursorBlink'], false);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
describe('setMode', () => {
|
||||||
|
it('should toggle Terminal.bracketedPasteMode', () => {
|
||||||
|
let terminal = new MockInputHandlingTerminal();
|
||||||
|
terminal.prefix = '?';
|
||||||
|
terminal.bracketedPasteMode = false;
|
||||||
|
let inputHandler = new InputHandler(terminal);
|
||||||
|
// Set bracketed paste mode
|
||||||
|
inputHandler.setMode([2004]);
|
||||||
|
assert.equal(terminal.bracketedPasteMode, true);
|
||||||
|
// Reset bracketed paste mode
|
||||||
|
inputHandler.resetMode([2004]);
|
||||||
|
assert.equal(terminal.bracketedPasteMode, false);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -930,6 +930,9 @@ export class InputHandler implements IInputHandler {
|
|||||||
this._terminal.viewport.syncScrollArea();
|
this._terminal.viewport.syncScrollArea();
|
||||||
this._terminal.showCursor();
|
this._terminal.showCursor();
|
||||||
break;
|
break;
|
||||||
|
case 2004: // bracketed paste mode (https://cirw.in/blog/bracketed-paste)
|
||||||
|
this._terminal.bracketedPasteMode = true;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1100,6 +1103,9 @@ export class InputHandler implements IInputHandler {
|
|||||||
this._terminal.viewport.syncScrollArea();
|
this._terminal.viewport.syncScrollArea();
|
||||||
this._terminal.showCursor();
|
this._terminal.showCursor();
|
||||||
break;
|
break;
|
||||||
|
case 2004: // bracketed paste mode (https://cirw.in/blog/bracketed-paste)
|
||||||
|
this._terminal.bracketedPasteMode = false;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -48,6 +48,7 @@ export interface ITerminal extends ILinkifierAccessor, IBufferAccessor, IElement
|
|||||||
buffers: IBufferSet;
|
buffers: IBufferSet;
|
||||||
isFocused: boolean;
|
isFocused: boolean;
|
||||||
mouseHelper: IMouseHelper;
|
mouseHelper: IMouseHelper;
|
||||||
|
bracketedPasteMode: boolean;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Emit the 'data' event and populate the given data.
|
* Emit the 'data' event and populate the given data.
|
||||||
@@ -82,6 +83,7 @@ export interface IInputHandlingTerminal extends IEventEmitter {
|
|||||||
originMode: boolean;
|
originMode: boolean;
|
||||||
insertMode: boolean;
|
insertMode: boolean;
|
||||||
wraparoundMode: boolean;
|
wraparoundMode: boolean;
|
||||||
|
bracketedPasteMode: boolean;
|
||||||
defAttr: number;
|
defAttr: number;
|
||||||
curAttr: number;
|
curAttr: number;
|
||||||
prefix: string;
|
prefix: string;
|
||||||
|
|||||||
@@ -132,6 +132,7 @@ export class Terminal extends EventEmitter implements ITerminal, IInputHandlingT
|
|||||||
public originMode: boolean;
|
public originMode: boolean;
|
||||||
public insertMode: boolean;
|
public insertMode: boolean;
|
||||||
public wraparoundMode: boolean; // defaults: xterm - true, vt100 - false
|
public wraparoundMode: boolean; // defaults: xterm - true, vt100 - false
|
||||||
|
public bracketedPasteMode: boolean;
|
||||||
|
|
||||||
// charset
|
// charset
|
||||||
// The current charset
|
// The current charset
|
||||||
@@ -260,6 +261,7 @@ export class Terminal extends EventEmitter implements ITerminal, IInputHandlingT
|
|||||||
this.originMode = false;
|
this.originMode = false;
|
||||||
this.insertMode = false;
|
this.insertMode = false;
|
||||||
this.wraparoundMode = true; // defaults: xterm - true, vt100 - false
|
this.wraparoundMode = true; // defaults: xterm - true, vt100 - false
|
||||||
|
this.bracketedPasteMode = false;
|
||||||
|
|
||||||
// charset
|
// charset
|
||||||
this.charset = null;
|
this.charset = null;
|
||||||
|
|||||||
@@ -16,4 +16,12 @@ describe('evaluatePastedTextProcessing', () => {
|
|||||||
assert.equal(processedText, 'foo\r\nbar\r\n');
|
assert.equal(processedText, 'foo\r\nbar\r\n');
|
||||||
assert.equal(windowsProcessedText, 'foo\rbar\r');
|
assert.equal(windowsProcessedText, 'foo\rbar\r');
|
||||||
});
|
});
|
||||||
|
it('should bracket pasted text in bracketedPasteMode', () => {
|
||||||
|
const pastedText = 'foo bar';
|
||||||
|
const unbracketedText = Clipboard.bracketTextForPaste(pastedText, false);
|
||||||
|
const bracketedText = Clipboard.bracketTextForPaste(pastedText, true);
|
||||||
|
|
||||||
|
assert.equal(unbracketedText, 'foo bar');
|
||||||
|
assert.equal(bracketedText, '\x1b[200~foo bar\x1b[201~');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -25,6 +25,17 @@ export function prepareTextForTerminal(text: string, isMSWindows: boolean): stri
|
|||||||
return text;
|
return text;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Bracket text for paste, if necessary, as per https://cirw.in/blog/bracketed-paste
|
||||||
|
* @param text The pasted text to bracket
|
||||||
|
*/
|
||||||
|
export function bracketTextForPaste(text: string, bracketedPasteMode: boolean): string {
|
||||||
|
if (bracketedPasteMode) {
|
||||||
|
return '\x1b[200~' + text + '\x1b[201~';
|
||||||
|
}
|
||||||
|
return text;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Binds copy functionality to the given terminal.
|
* Binds copy functionality to the given terminal.
|
||||||
* @param {ClipboardEvent} ev The original copy event to be handled
|
* @param {ClipboardEvent} ev The original copy event to be handled
|
||||||
@@ -52,6 +63,7 @@ export function pasteHandler(ev: ClipboardEvent, term: ITerminal): void {
|
|||||||
|
|
||||||
let dispatchPaste = function(text: string): void {
|
let dispatchPaste = function(text: string): void {
|
||||||
text = prepareTextForTerminal(text, term.browser.isMSWindows);
|
text = prepareTextForTerminal(text, term.browser.isMSWindows);
|
||||||
|
text = bracketTextForPaste(text, term.bracketedPasteMode);
|
||||||
term.handler(text);
|
term.handler(text);
|
||||||
term.textarea.value = '';
|
term.textarea.value = '';
|
||||||
term.emit('paste', text);
|
term.emit('paste', text);
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import * as Browser from './Browser';
|
|||||||
import { IColorSet, IRenderer, IRenderDimensions, IColorManager } from '../renderer/Interfaces';
|
import { IColorSet, IRenderer, IRenderDimensions, IColorManager } from '../renderer/Interfaces';
|
||||||
|
|
||||||
export class MockTerminal implements ITerminal {
|
export class MockTerminal implements ITerminal {
|
||||||
|
bracketedPasteMode: boolean;
|
||||||
mouseHelper: IMouseHelper;
|
mouseHelper: IMouseHelper;
|
||||||
renderer: IRenderer;
|
renderer: IRenderer;
|
||||||
linkifier: ILinkifier;
|
linkifier: ILinkifier;
|
||||||
@@ -94,6 +95,7 @@ export class MockInputHandlingTerminal implements IInputHandlingTerminal {
|
|||||||
originMode: boolean;
|
originMode: boolean;
|
||||||
insertMode: boolean;
|
insertMode: boolean;
|
||||||
wraparoundMode: boolean;
|
wraparoundMode: boolean;
|
||||||
|
bracketedPasteMode: boolean;
|
||||||
defAttr: number;
|
defAttr: number;
|
||||||
curAttr: number;
|
curAttr: number;
|
||||||
prefix: string;
|
prefix: string;
|
||||||
|
|||||||
Reference in New Issue
Block a user