Path: blob/main/src/vs/editor/test/browser/controller/imeTester.ts
3296 views
/*---------------------------------------------------------------------------------------------1* Copyright (c) Microsoft Corporation. All rights reserved.2* Licensed under the MIT License. See License.txt in the project root for license information.3*--------------------------------------------------------------------------------------------*/45import { Position } from '../../../common/core/position.js';6import { IRange, Range } from '../../../common/core/range.js';7import { EndOfLinePreference } from '../../../common/model.js';8import * as dom from '../../../../base/browser/dom.js';9import * as browser from '../../../../base/browser/browser.js';10import * as platform from '../../../../base/common/platform.js';11import { mainWindow } from '../../../../base/browser/window.js';12import { TestAccessibilityService } from '../../../../platform/accessibility/test/common/testAccessibilityService.js';13import { NullLogService } from '../../../../platform/log/common/log.js';14import { SimplePagedScreenReaderStrategy } from '../../../browser/controller/editContext/screenReaderUtils.js';15import { ISimpleModel } from '../../../common/viewModel/screenReaderSimpleModel.js';16import { TextAreaState } from '../../../browser/controller/editContext/textArea/textAreaEditContextState.js';17import { ITextAreaInputHost, TextAreaInput, TextAreaWrapper } from '../../../browser/controller/editContext/textArea/textAreaEditContextInput.js';18import { Selection } from '../../../common/core/selection.js';1920// To run this test, open imeTester.html2122class SingleLineTestModel implements ISimpleModel {2324private _line: string;2526constructor(line: string) {27this._line = line;28}2930_setText(text: string) {31this._line = text;32}3334getLineMaxColumn(lineNumber: number): number {35return this._line.length + 1;36}3738getValueInRange(range: IRange, eol: EndOfLinePreference): string {39return this._line.substring(range.startColumn - 1, range.endColumn - 1);40}4142getValueLengthInRange(range: Range, eol: EndOfLinePreference): number {43return this.getValueInRange(range, eol).length;44}4546modifyPosition(position: Position, offset: number): Position {47const column = Math.min(this.getLineMaxColumn(position.lineNumber), Math.max(1, position.column + offset));48return new Position(position.lineNumber, column);49}5051getModelLineContent(lineNumber: number): string {52return this._line;53}5455getLineCount(): number {56return 1;57}58}5960class TestView {6162private readonly _model: SingleLineTestModel;6364constructor(model: SingleLineTestModel) {65this._model = model;66}6768public paint(output: HTMLElement) {69dom.clearNode(output);70for (let i = 1; i <= this._model.getLineCount(); i++) {71const textNode = document.createTextNode(this._model.getModelLineContent(i));72output.appendChild(textNode);73const br = document.createElement('br');74output.appendChild(br);75}76}77}7879function doCreateTest(description: string, inputStr: string, expectedStr: string): HTMLElement {80let cursorOffset: number = 0;81let cursorLength: number = 0;8283const container = document.createElement('div');84container.className = 'container';8586const title = document.createElement('div');87title.className = 'title';8889const inputStrStrong = document.createElement('strong');90inputStrStrong.innerText = inputStr;9192title.innerText = description + '. Type ';93title.appendChild(inputStrStrong);9495container.appendChild(title);9697const startBtn = document.createElement('button');98startBtn.innerText = 'Start';99container.appendChild(startBtn);100101102const input = document.createElement('textarea');103input.setAttribute('rows', '10');104input.setAttribute('cols', '40');105container.appendChild(input);106107const model = new SingleLineTestModel('some text');108const screenReaderStrategy = new SimplePagedScreenReaderStrategy();109const textAreaInputHost: ITextAreaInputHost = {110getDataToCopy: () => {111return {112isFromEmptySelection: false,113multicursorText: null,114text: '',115html: undefined,116mode: null117};118},119getScreenReaderContent: (): TextAreaState => {120const selection = new Selection(1, 1 + cursorOffset, 1, 1 + cursorOffset + cursorLength);121122const screenReaderContentState = screenReaderStrategy.fromEditorSelection(model, selection, 10, true);123return TextAreaState.fromScreenReaderContentState(screenReaderContentState);124},125deduceModelPosition: (viewAnchorPosition: Position, deltaOffset: number, lineFeedCnt: number): Position => {126return null!;127}128};129130const handler = new TextAreaInput(textAreaInputHost, new TextAreaWrapper(input), platform.OS, {131isAndroid: browser.isAndroid,132isFirefox: browser.isFirefox,133isChrome: browser.isChrome,134isSafari: browser.isSafari,135}, new TestAccessibilityService(), new NullLogService());136137const output = document.createElement('pre');138output.className = 'output';139container.appendChild(output);140141const check = document.createElement('pre');142check.className = 'check';143container.appendChild(check);144145const br = document.createElement('br');146br.style.clear = 'both';147container.appendChild(br);148149const view = new TestView(model);150151const updatePosition = (off: number, len: number) => {152cursorOffset = off;153cursorLength = len;154handler.writeNativeTextAreaContent('selection changed');155handler.focusTextArea();156};157158const updateModelAndPosition = (text: string, off: number, len: number) => {159model._setText(text);160updatePosition(off, len);161view.paint(output);162163const expected = 'some ' + expectedStr + ' text';164if (text === expected) {165check.innerText = '[GOOD]';166check.className = 'check good';167} else {168check.innerText = '[BAD]';169check.className = 'check bad';170}171check.appendChild(document.createTextNode(expected));172};173174handler.onType((e) => {175console.log('type text: ' + e.text + ', replaceCharCnt: ' + e.replacePrevCharCnt);176const text = model.getModelLineContent(1);177const preText = text.substring(0, cursorOffset - e.replacePrevCharCnt);178const postText = text.substring(cursorOffset + cursorLength);179const midText = e.text;180181updateModelAndPosition(preText + midText + postText, (preText + midText).length, 0);182});183184view.paint(output);185186startBtn.onclick = function () {187updateModelAndPosition('some text', 5, 0);188input.focus();189};190191return container;192}193194const TESTS = [195{ description: 'Japanese IME 1', in: 'sennsei [Enter]', out: 'せんせい' },196{ description: 'Japanese IME 2', in: 'konnichiha [Enter]', out: 'こんいちは' },197{ description: 'Japanese IME 3', in: 'mikann [Enter]', out: 'みかん' },198{ description: 'Korean IME 1', in: 'gksrmf [Space]', out: '한글 ' },199{ description: 'Chinese IME 1', in: '.,', out: '。,' },200{ description: 'Chinese IME 2', in: 'ni [Space] hao [Space]', out: '你好' },201{ description: 'Chinese IME 3', in: 'hazni [Space]', out: '哈祝你' },202{ description: 'Mac dead key 1', in: '`.', out: '`.' },203{ description: 'Mac hold key 1', in: 'e long press and 1', out: 'é' }204];205206TESTS.forEach((t) => {207mainWindow.document.body.appendChild(doCreateTest(t.description, t.in, t.out));208});209210211