in src/vs/editor/browser/controller/textAreaHandler.ts [115:335]
constructor(context: ViewContext, viewController: ViewController, viewHelper: ITextAreaHandlerHelper) {
super(context);
this._viewController = viewController;
this._viewHelper = viewHelper;
const conf = this._context.configuration.editor;
this._accessibilitySupport = conf.accessibilitySupport;
this._contentLeft = conf.layoutInfo.contentLeft;
this._contentWidth = conf.layoutInfo.contentWidth;
this._contentHeight = conf.layoutInfo.contentHeight;
this._scrollLeft = 0;
this._scrollTop = 0;
this._fontInfo = conf.fontInfo;
this._lineHeight = conf.lineHeight;
this._emptySelectionClipboard = conf.emptySelectionClipboard;
this._copyWithSyntaxHighlighting = conf.copyWithSyntaxHighlighting;
this._visibleTextArea = null;
this._selections = [new Selection(1, 1, 1, 1)];
// Text Area (The focus will always be in the textarea when the cursor is blinking)
this.textArea = createFastDomNode(document.createElement('textarea'));
PartFingerprints.write(this.textArea, PartFingerprint.TextArea);
this.textArea.setClassName('inputarea');
this.textArea.setAttribute('wrap', 'off');
this.textArea.setAttribute('autocorrect', 'off');
this.textArea.setAttribute('autocapitalize', 'off');
this.textArea.setAttribute('autocomplete', 'off');
this.textArea.setAttribute('spellcheck', 'false');
this.textArea.setAttribute('aria-label', conf.viewInfo.ariaLabel);
this.textArea.setAttribute('role', 'textbox');
this.textArea.setAttribute('aria-multiline', 'true');
this.textArea.setAttribute('aria-haspopup', 'false');
this.textArea.setAttribute('aria-autocomplete', 'both');
this.textAreaCover = createFastDomNode(document.createElement('div'));
this.textAreaCover.setPosition('absolute');
const simpleModel: ISimpleModel = {
getLineCount: (): number => {
return this._context.model.getLineCount();
},
getLineMaxColumn: (lineNumber: number): number => {
return this._context.model.getLineMaxColumn(lineNumber);
},
getValueInRange: (range: Range, eol: EndOfLinePreference): string => {
return this._context.model.getValueInRange(range, eol);
}
};
const textAreaInputHost: ITextAreaInputHost = {
getPlainTextToCopy: (): string => {
const rawWhatToCopy = this._context.model.getPlainTextToCopy(this._selections, this._emptySelectionClipboard, platform.isWindows);
const newLineCharacter = this._context.model.getEOL();
const isFromEmptySelection = (this._emptySelectionClipboard && this._selections.length === 1 && this._selections[0].isEmpty());
const multicursorText = (Array.isArray(rawWhatToCopy) ? rawWhatToCopy : null);
const whatToCopy = (Array.isArray(rawWhatToCopy) ? rawWhatToCopy.join(newLineCharacter) : rawWhatToCopy);
let metadata: LocalClipboardMetadata | null = null;
if (isFromEmptySelection || multicursorText) {
// Only store the non-default metadata
// When writing "LINE\r\n" to the clipboard and then pasting,
// Firefox pastes "LINE\n", so let's work around this quirk
const lastCopiedValue = (browser.isFirefox ? whatToCopy.replace(/\r\n/g, '\n') : whatToCopy);
metadata = {
lastCopiedValue: lastCopiedValue,
isFromEmptySelection: (this._emptySelectionClipboard && this._selections.length === 1 && this._selections[0].isEmpty()),
multicursorText: multicursorText
};
}
LocalClipboardMetadataManager.INSTANCE.set(metadata);
return whatToCopy;
},
getHTMLToCopy: (): string | null => {
if (!this._copyWithSyntaxHighlighting && !CopyOptions.forceCopyWithSyntaxHighlighting) {
return null;
}
return this._context.model.getHTMLToCopy(this._selections, this._emptySelectionClipboard);
},
getScreenReaderContent: (currentState: TextAreaState): TextAreaState => {
if (browser.isIPad) {
// Do not place anything in the textarea for the iPad
return TextAreaState.EMPTY;
}
if (this._accessibilitySupport === AccessibilitySupport.Disabled) {
// We know for a fact that a screen reader is not attached
// On OSX, we write the character before the cursor to allow for "long-press" composition
// Also on OSX, we write the word before the cursor to allow for the Accessibility Keyboard to give good hints
if (platform.isMacintosh) {
const selection = this._selections[0];
if (selection.isEmpty()) {
const position = selection.getStartPosition();
let textBefore = this._getWordBeforePosition(position);
if (textBefore.length === 0) {
textBefore = this._getCharacterBeforePosition(position);
}
if (textBefore.length > 0) {
return new TextAreaState(textBefore, textBefore.length, textBefore.length, position, position);
}
}
}
return TextAreaState.EMPTY;
}
return PagedScreenReaderStrategy.fromEditorSelection(currentState, simpleModel, this._selections[0], this._accessibilitySupport === AccessibilitySupport.Unknown);
},
deduceModelPosition: (viewAnchorPosition: Position, deltaOffset: number, lineFeedCnt: number): Position => {
return this._context.model.deduceModelPositionRelativeToViewPosition(viewAnchorPosition, deltaOffset, lineFeedCnt);
}
};
this._textAreaInput = this._register(new TextAreaInput(textAreaInputHost, this.textArea));
this._register(this._textAreaInput.onKeyDown((e: IKeyboardEvent) => {
this._viewController.emitKeyDown(e);
}));
this._register(this._textAreaInput.onKeyUp((e: IKeyboardEvent) => {
this._viewController.emitKeyUp(e);
}));
this._register(this._textAreaInput.onPaste((e: IPasteData) => {
const metadata = LocalClipboardMetadataManager.INSTANCE.get(e.text);
let pasteOnNewLine = false;
let multicursorText: string[] | null = null;
if (metadata) {
pasteOnNewLine = (this._emptySelectionClipboard && metadata.isFromEmptySelection);
multicursorText = metadata.multicursorText;
}
this._viewController.paste('keyboard', e.text, pasteOnNewLine, multicursorText);
}));
this._register(this._textAreaInput.onCut(() => {
this._viewController.cut('keyboard');
}));
this._register(this._textAreaInput.onType((e: ITypeData) => {
if (e.replaceCharCnt) {
this._viewController.replacePreviousChar('keyboard', e.text, e.replaceCharCnt);
} else {
this._viewController.type('keyboard', e.text);
}
}));
this._register(this._textAreaInput.onSelectionChangeRequest((modelSelection: Selection) => {
this._viewController.setSelection('keyboard', modelSelection);
}));
this._register(this._textAreaInput.onCompositionStart(() => {
const lineNumber = this._selections[0].startLineNumber;
const column = this._selections[0].startColumn;
this._context.privateViewEventBus.emit(new viewEvents.ViewRevealRangeRequestEvent(
new Range(lineNumber, column, lineNumber, column),
viewEvents.VerticalRevealType.Simple,
true,
ScrollType.Immediate
));
// Find range pixel position
const visibleRange = this._viewHelper.visibleRangeForPositionRelativeToEditor(lineNumber, column);
if (visibleRange) {
this._visibleTextArea = new VisibleTextAreaData(
this._context.viewLayout.getVerticalOffsetForLineNumber(lineNumber),
visibleRange.left,
canUseZeroSizeTextarea ? 0 : 1
);
this._render();
}
// Show the textarea
this.textArea.setClassName('inputarea ime-input');
this._viewController.compositionStart('keyboard');
}));
this._register(this._textAreaInput.onCompositionUpdate((e: ICompositionData) => {
if (browser.isEdgeOrIE) {
// Due to isEdgeOrIE (where the textarea was not cleared initially)
// we cannot assume the text consists only of the composited text
this._visibleTextArea = this._visibleTextArea!.setWidth(0);
} else {
// adjust width by its size
this._visibleTextArea = this._visibleTextArea!.setWidth(measureText(e.data, this._fontInfo));
}
this._render();
}));
this._register(this._textAreaInput.onCompositionEnd(() => {
this._visibleTextArea = null;
this._render();
this.textArea.setClassName('inputarea');
this._viewController.compositionEnd('keyboard');
}));
this._register(this._textAreaInput.onFocus(() => {
this._context.privateViewEventBus.emit(new viewEvents.ViewFocusChangedEvent(true));
}));
this._register(this._textAreaInput.onBlur(() => {
this._context.privateViewEventBus.emit(new viewEvents.ViewFocusChangedEvent(false));
}));
}