提交 0a893ff4 编写于 作者: R rebornix

use inline styles

上级 dd0558f2
......@@ -56,7 +56,7 @@ export class KeyboardHandler extends ViewEventHandler implements IDisposable {
this.contentWidth = 0;
this.scrollLeft = 0;
this.textAreaHandler = new TextAreaHandler(browser, this._getStrategy(), this.textArea, this._context.model, this._context.configuration.editor, () => this.viewHelper.flushAnyAccumulatedEvents());
this.textAreaHandler = new TextAreaHandler(browser, this._getStrategy(), this.textArea, this._context.model, () => this.viewHelper.flushAnyAccumulatedEvents());
this._toDispose = [];
this._toDispose.push(this.textAreaHandler.onKeyDown((e) => this.viewController.emitKeyDown(<IKeyboardEvent>e._actual)));
......
......@@ -11,10 +11,6 @@ import { KeyCode } from 'vs/base/common/keyCodes';
import { Disposable } from 'vs/base/common/lifecycle';
import { IClipboardEvent, ICompositionEvent, IKeyboardEventWrapper, ISimpleModel, ITextAreaWrapper, ITypeData, TextAreaState, TextAreaStrategy, createTextAreaState } from 'vs/editor/common/controller/textAreaState';
import { Range } from 'vs/editor/common/core/range';
import { Position } from 'vs/editor/common/core/position';
import { EndOfLinePreference, InternalEditorOptions } from 'vs/editor/common/editorCommon';
import { TokenizationRegistry } from 'vs/editor/common/modes';
import { renderViewLine, RenderLineInput } from 'vs/editor/common/viewLayout/viewLineRenderer';
const enum ReadFromTextArea {
Type,
......@@ -75,7 +71,6 @@ export class TextAreaHandler extends Disposable {
private Browser: IBrowser;
private textArea: ITextAreaWrapper;
private model: ISimpleModel;
private config: InternalEditorOptions;
private flushAnyAccumulatedEvents: () => void;
private selection: Range;
......@@ -94,12 +89,11 @@ export class TextAreaHandler extends Disposable {
private _nextCommand: ReadFromTextArea;
constructor(Browser: IBrowser, strategy: TextAreaStrategy, textArea: ITextAreaWrapper, model: ISimpleModel, config: InternalEditorOptions, flushAnyAccumulatedEvents: () => void) {
constructor(Browser: IBrowser, strategy: TextAreaStrategy, textArea: ITextAreaWrapper, model: ISimpleModel, flushAnyAccumulatedEvents: () => void) {
super();
this.Browser = Browser;
this.textArea = textArea;
this.model = model;
this.config = config;
this.flushAnyAccumulatedEvents = flushAnyAccumulatedEvents;
this.selection = new Range(1, 1, 1, 1);
this.selections = [new Range(1, 1, 1, 1)];
......@@ -328,14 +322,10 @@ export class TextAreaHandler extends Disposable {
// ------------- Clipboard operations
private _ensureClipboardGetsEditorSelection(e: IClipboardEvent): void {
let whatToCopy = this._getPlainTextToCopy();
let whatToCopy = this.model.getPlainTextToCopy(this.selections, this.Browser.enableEmptySelectionClipboard);
if (e.canUseTextData()) {
if (this.config.contribInfo.richTextClipboard) {
let whatRichTextToCopy = this._getHTMLToCopy();
e.setTextData(whatToCopy, whatRichTextToCopy === undefined ? whatToCopy : whatRichTextToCopy);
} else {
e.setTextData(whatToCopy);
}
let whatHTMLToCopy = this.model.getHTMLToCopy(this.selections, this.Browser.enableEmptySelectionClipboard);
e.setTextData(whatToCopy, whatHTMLToCopy);
} else {
this.setTextAreaState('copy or cut', this.textAreaState.fromText(whatToCopy), false);
}
......@@ -353,86 +343,4 @@ export class TextAreaHandler extends Disposable {
this.lastCopiedValueIsFromEmptySelection = (selections.length === 1 && selections[0].isEmpty());
}
}
private _getPlainTextToCopy(): string {
let newLineCharacter = this.model.getEOL();
let selections = this.selections;
if (selections.length === 1) {
let range: Range = selections[0];
if (range.isEmpty()) {
if (this.Browser.enableEmptySelectionClipboard) {
let modelLineNumber = this.model.coordinatesConverter.convertViewPositionToModelPosition(new Position(range.startLineNumber, 1)).lineNumber;
return this.model.getModelLineContent(modelLineNumber) + newLineCharacter;
} else {
return '';
}
}
return this.model.getValueInRange(range, EndOfLinePreference.TextDefined);
} else {
selections = selections.slice(0).sort(Range.compareRangesUsingStarts);
let result: string[] = [];
for (let i = 0; i < selections.length; i++) {
result.push(this.model.getValueInRange(selections[i], EndOfLinePreference.TextDefined));
}
return result.join(newLineCharacter);
}
}
private _getHTMLToCopy(): string | undefined {
if (!this.config) {
return undefined;
}
let selections = this.selections;
let rules: string[] = [];
let colorMap = TokenizationRegistry.getColorMap();
for (let i = 1, len = colorMap.length; i < len; i++) {
let color = colorMap[i];
if (/^(?:[0-9a-fA-F]{3}){1,2}$/.test(color)) {
color = '#' + color;
}
rules[i] = `.mtk${i} { color: ${color}; }`;
}
rules.push('.mtki { font-style: italic; }');
rules.push('.mtkb { font-weight: bold; }');
rules.push('.mtku { text-decoration: underline; }');
let output = `<style>${rules.join('\n')}</style>`;
output += '<div class="monaco-editor-background">';
if (selections.length === 1) {
let range: Range = selections[0];
for (let i = 0, lineCount = range.endLineNumber - range.startLineNumber; i <= lineCount; i++) {
let viewLineRenderingData = this.model.getViewLineRenderingData(range, range.startLineNumber + i);
let lineContent = viewLineRenderingData.content;
let startOffset = i === 0 ? range.startColumn - 1 : 0;
let endOffset = i === lineCount ? range.endColumn - 1 : lineContent.length;
lineContent = viewLineRenderingData.content.substring(startOffset, endOffset);
let r = renderViewLine(new RenderLineInput(
(this.config.fontInfo.isMonospace && !this.config.viewInfo.disableMonospaceOptimizations),
lineContent,
viewLineRenderingData.mightContainRTL,
0,
viewLineRenderingData.tokens,
[],
viewLineRenderingData.tabSize,
this.config.fontInfo.spaceWidth,
endOffset,
'none',
false
));
output += `<div>${r.html}</div>`;
}
}
output += '</div>';
return output;
}
}
}
\ No newline at end of file
......@@ -10,7 +10,6 @@ import { Range } from 'vs/editor/common/core/range';
import { EndOfLinePreference } from 'vs/editor/common/editorCommon';
import { Position } from 'vs/editor/common/core/position';
import { Constants } from 'vs/editor/common/core/uint';
import { ViewLineRenderingData } from 'vs/editor/common/viewModel/viewModel';
export interface IClipboardEvent {
canUseTextData(): boolean;
......@@ -57,7 +56,8 @@ export interface ISimpleModel {
getValueInRange(range: Range, eol: EndOfLinePreference): string;
getModelLineContent(lineNumber: number): string;
getLineCount(): number;
getViewLineRenderingData(visibleRange: Range, lineNumber: number): ViewLineRenderingData;
getPlainTextToCopy(ranges: Range[], enableEmptySelectionClipboard: boolean): string;
getHTMLToCopy(ranges: Range[], enableEmptySelectionClipboard: boolean): string;
coordinatesConverter: {
convertViewPositionToModelPosition(viewPosition: Position): Position;
......
......@@ -8,11 +8,115 @@ import * as strings from 'vs/base/common/strings';
import { IState, ITokenizationSupport, TokenizationRegistry, LanguageId } from 'vs/editor/common/modes';
import { NULL_STATE, nullTokenize2 } from 'vs/editor/common/modes/nullMode';
import { LineTokens } from 'vs/editor/common/core/lineTokens';
import { CharacterMapping } from 'vs/editor/common/viewLayout/viewLineRenderer';
import { CharCode } from 'vs/base/common/charCode';
import { ViewLineToken } from 'vs/editor/common/core/viewLineToken';
export function tokenizeToString(text: string, languageId: string): string {
return _tokenizeToString(text, _getSafeTokenizationSupport(languageId));
}
export function tokenizeLineToHTML(text: string, viewLineTokens: ViewLineToken[], rules: { [key: string]: string }, options: { startOffset: number, endOffset: number, tabSize: number, containsRTL: boolean }): string {
let tabSize = options.tabSize;
let containsRTL = options.containsRTL;
let result = `<div>`;
const characterMapping = new CharacterMapping(text.length + 1, viewLineTokens.length);
let charIndex = options.startOffset;
let tabsCharDelta = 0;
let charOffsetInPart = 0;
for (let tokenIndex = 0, lenJ = viewLineTokens.length; tokenIndex < lenJ; tokenIndex++) {
const token = viewLineTokens[tokenIndex];
const tokenEndIndex = token.endIndex;
if (token.endIndex < options.startOffset) {
continue;
}
const tokenType = token.type;
let partContentCnt = 0;
let partContent = '';
for (; charIndex < tokenEndIndex && charIndex < options.endOffset; charIndex++) {
characterMapping.setPartData(charIndex, tokenIndex, charOffsetInPart);
const charCode = text.charCodeAt(charIndex);
switch (charCode) {
case CharCode.Tab:
let insertSpacesCount = tabSize - (charIndex + tabsCharDelta) % tabSize;
tabsCharDelta += insertSpacesCount - 1;
charOffsetInPart += insertSpacesCount - 1;
while (insertSpacesCount > 0) {
partContent += '&nbsp;';
partContentCnt++;
insertSpacesCount--;
}
break;
case CharCode.Space:
partContent += '&nbsp;';
partContentCnt++;
break;
case CharCode.LessThan:
partContent += '&lt;';
partContentCnt++;
break;
case CharCode.GreaterThan:
partContent += '&gt;';
partContentCnt++;
break;
case CharCode.Ampersand:
partContent += '&amp;';
partContentCnt++;
break;
case CharCode.Null:
partContent += '&#00;';
partContentCnt++;
break;
case CharCode.UTF8_BOM:
case CharCode.LINE_SEPARATOR_2028:
partContent += '\ufffd';
partContentCnt++;
break;
case CharCode.CarriageReturn:
// zero width space, because carriage return would introduce a line break
partContent += '&#8203';
partContentCnt++;
break;
default:
partContent += String.fromCharCode(charCode);
partContentCnt++;
}
charOffsetInPart++;
}
characterMapping.setPartLength(tokenIndex, partContentCnt);
let style = tokenType.split(' ').map(type => rules[type]).join('');
if (containsRTL) {
result += `<span dir="ltr" style="${style}">${partContent}</span>`;
} else {
result += `<span style="${style}">${partContent}</span>`;
}
if (token.endIndex > options.endOffset) {
break;
}
}
result += `</div>`;
return result;
}
function _getSafeTokenizationSupport(languageId: string): ITokenizationSupport {
let tokenizationSupport = TokenizationRegistry.get(languageId);
if (tokenizationSupport) {
......
......@@ -87,6 +87,9 @@ export interface IViewModel extends IEventEmitter {
getModelLineContent(modelLineNumber: number): string;
getModelLineMaxColumn(modelLineNumber: number): number;
validateModelPosition(modelPosition: IPosition): Position;
getPlainTextToCopy(ranges: Range[], enableEmptySelectionClipboard: boolean): string;
getHTMLToCopy(ranges: Range[], enableEmptySelectionClipboard: boolean): string;
}
export class ViewLineRenderingData {
......
......@@ -11,6 +11,8 @@ import { Position } from 'vs/editor/common/core/position';
import { Range } from 'vs/editor/common/core/range';
import { Selection } from 'vs/editor/common/core/selection';
import * as editorCommon from 'vs/editor/common/editorCommon';
import { TokenizationRegistry } from 'vs/editor/common/modes';
import { tokenizeLineToHTML } from 'vs/editor/common/modes/textToHtmlTokenizer';
import { ViewModelCursors } from 'vs/editor/common/viewModel/viewModelCursors';
import { ViewModelDecorations } from 'vs/editor/common/viewModel/viewModelDecorations';
import { ViewLineRenderingData, ViewModelDecoration, IViewModel, ICoordinatesConverter } from 'vs/editor/common/viewModel/viewModel';
......@@ -558,4 +560,97 @@ export class ViewModel extends EventEmitter implements IViewModel {
public validateModelPosition(position: editorCommon.IPosition): Position {
return this.model.validatePosition(position);
}
public getPlainTextToCopy(ranges: Range[], enableEmptySelectionClipboard: boolean): string {
let newLineCharacter = this.getEOL();
if (ranges.length === 1) {
let range: Range = ranges[0];
if (range.isEmpty()) {
if (enableEmptySelectionClipboard) {
let modelLineNumber = this.coordinatesConverter.convertViewPositionToModelPosition(new Position(range.startLineNumber, 1)).lineNumber;
return this.getModelLineContent(modelLineNumber) + newLineCharacter;
} else {
return '';
}
}
return this.getValueInRange(range, editorCommon.EndOfLinePreference.TextDefined);
} else {
ranges = ranges.slice(0).sort(Range.compareRangesUsingStarts);
let result: string[] = [];
for (let i = 0; i < ranges.length; i++) {
result.push(this.getValueInRange(ranges[i], editorCommon.EndOfLinePreference.TextDefined));
}
return result.join(newLineCharacter);
}
}
public getHTMLToCopy(ranges: Range[], enableEmptySelectionClipboard: boolean): string {
let rules: { [key: string]: string } = {};
let colorMap = TokenizationRegistry.getColorMap();
for (let i = 1, len = colorMap.length; i < len; i++) {
let color = colorMap[i];
if (/^(?:[0-9a-fA-F]{3}){1,2}$/.test(color)) {
color = '#' + color;
}
rules[`mtk${i}`] = `color: ${color};`;
}
rules['mtki'] = 'font-style: italic;';
rules['mtkb'] = 'font-weight: bold;';
rules['mtku'] = 'text-decoration: underline;';
let defaultForegroundColor = /^(?:[0-9a-fA-F]{3}){1,2}$/.test(colorMap[1]) ? '#' + colorMap[1] : colorMap[1];
let defaultBackgroundColor = /^(?:[0-9a-fA-F]{3}){1,2}$/.test(colorMap[2]) ? '#' + colorMap[2] : colorMap[2];
let output = `<div style="color: ${defaultForegroundColor}; background-color: ${defaultBackgroundColor}">`;
if (ranges.length === 1) {
let range: Range = ranges[0];
if (range.isEmpty()) {
if (enableEmptySelectionClipboard) {
let modelLineNumber = this.coordinatesConverter.convertViewPositionToModelPosition(new Position(range.startLineNumber, 1)).lineNumber;
let viewLineStart = new Position(range.startLineNumber, 1);
let viewLineEnd = new Position(range.startLineNumber, this.getLineMaxColumn(range.startLineNumber));
let startOffset = this.coordinatesConverter.convertViewPositionToModelPosition(viewLineStart).column - 1;
let endOffset = this.coordinatesConverter.convertViewPositionToModelPosition(viewLineEnd).column - 1;
let viewLineRenderingData = this.getViewLineRenderingData(new Range(viewLineStart.lineNumber, viewLineStart.column, viewLineEnd.lineNumber, viewLineEnd.column), modelLineNumber);
let html = tokenizeLineToHTML(this.getModelLineContent(modelLineNumber),
viewLineRenderingData.tokens,
rules,
{
startOffset: startOffset,
endOffset: endOffset,
tabSize: this.getTabSize(),
containsRTL: this.model.mightContainRTL()
});
output += `${html}`;
} else {
return '';
}
} else {
for (let i = 0, lineCount = range.endLineNumber - range.startLineNumber; i <= lineCount; i++) {
let viewLineRenderingData = this.getViewLineRenderingData(range, range.startLineNumber + i);
let lineContent = viewLineRenderingData.content;
let startOffset = i === 0 ? range.startColumn - 1 : 0;
let endOffset = i === lineCount ? range.endColumn - 1 : lineContent.length;
let html = tokenizeLineToHTML(lineContent, viewLineRenderingData.tokens, rules,
{
startOffset: startOffset,
endOffset: endOffset,
tabSize: this.getTabSize(),
containsRTL: this.model.mightContainRTL()
});
output += `${html}`;
}
}
}
output += '</div>';
return output;
}
}
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as editorCommon from 'vs/editor/common/editorCommon';
import { Disposable } from 'vs/base/common/lifecycle';
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
import { editorContribution } from 'vs/editor/browser/editorBrowserExtensions';
import { IModeService } from 'vs/editor/common/services/modeService';
@editorContribution
class CopyRichTextController extends Disposable implements editorCommon.IEditorContribution {
private static ID = 'editor.contrib.copyRichText';
public static get(editor: editorCommon.ICommonCodeEditor): CopyRichTextController {
return editor.getContribution<CopyRichTextController>(CopyRichTextController.ID);
}
private _editor: ICodeEditor;
private _modeService: IModeService;
private _handler: (event: any) => void;
constructor(
editor: ICodeEditor,
@IModeService modeService: IModeService,
) {
super();
this._editor = editor;
this._modeService = modeService;
this._handler = (e: ClipboardEvent) => {
if (e.target instanceof HTMLElement) {
const target = <HTMLElement>e.target;
if (target.nodeName && (target.nodeName.toLowerCase() === 'input' || target.nodeName.toLowerCase() === 'textarea')) {
let richText = e.clipboardData.getData('text/html');
if (richText) {
let themeId = this._editor.getConfiguration().viewInfo.theme;
let color: string;
if (/vs-dark($| )/.test(themeId)) {
color = 'color: #BBB;background: #1E1E1E;';
} if (/vs($| )/.test(themeId)) {
color = 'color: #333;background: #fffffe;';
} else {
color = 'color: #fff;background: #000;';
}
e.clipboardData.setData('text/html', `<style>.monaco-editor-background { ${color} }</style>\n${richText}`);
}
}
}
};
document.addEventListener('copy', this._handler);
}
public getId(): string {
return CopyRichTextController.ID;
}
public dispose(): void {
document.removeEventListener('copy', this._handler);
super.dispose();
}
}
\ No newline at end of file
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as editorCommon from 'vs/editor/common/editorCommon';
import { Disposable } from 'vs/base/common/lifecycle';
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
import { editorContribution } from 'vs/editor/browser/editorBrowserExtensions';
import { IModeService } from 'vs/editor/common/services/modeService';
import { IThemeService } from 'vs/workbench/services/themes/common/themeService';
@editorContribution
class CopyRichTextController extends Disposable implements editorCommon.IEditorContribution {
private static ID = 'editor.contrib.copyRichText';
public static get(editor: editorCommon.ICommonCodeEditor): CopyRichTextController {
return editor.getContribution<CopyRichTextController>(CopyRichTextController.ID);
}
private _editor: ICodeEditor;
private _themeService: IThemeService;
private _modeService: IModeService;
private _handler: (event: any) => void;
constructor(
editor: ICodeEditor,
@IModeService modeService: IModeService,
@IThemeService themeService: IThemeService
) {
super();
this._editor = editor;
this._themeService = themeService;
this._modeService = modeService;
this._handler = (e: ClipboardEvent) => {
if (e.target instanceof HTMLElement) {
const target = <HTMLElement>e.target;
if (target.nodeName && (target.nodeName.toLowerCase() === 'input' || target.nodeName.toLowerCase() === 'textarea')) {
let richText = e.clipboardData.getData('text/html');
if (richText) {
let theme = this._themeService.getColorTheme();
let globalSettings = theme.settings.filter(s => !s.scope);
if (globalSettings.length > 0) {
let backgroundColor = globalSettings[0].settings.background;
e.clipboardData.setData('text/html', `<style>.monaco-editor-background { background-color: ${backgroundColor} }</style>\n${richText}`);
}
}
}
}
};
window.document.addEventListener('copy', this._handler);
}
public getId(): string {
return CopyRichTextController.ID;
}
public dispose(): void {
window.document.removeEventListener('copy', this._handler);
super.dispose();
}
}
\ No newline at end of file
......@@ -10,7 +10,6 @@ import 'vs/editor/contrib/quickOpen/browser/quickOutline';
import 'vs/editor/contrib/quickOpen/browser/gotoLine';
import 'vs/editor/contrib/quickOpen/browser/quickCommand';
import 'vs/editor/contrib/inspectTokens/browser/inspectTokens';
import 'vs/editor/contrib/clipboard/browser/standaloneRichClipboard';
import { createMonacoBaseAPI } from 'vs/editor/common/standalone/standaloneBase';
import { createMonacoEditorAPI } from 'vs/editor/browser/standalone/standaloneEditor';
......
......@@ -11,7 +11,6 @@ import { Range } from 'vs/editor/common/core/range';
import * as editorCommon from 'vs/editor/common/editorCommon';
import { TextAreaWrapper } from 'vs/editor/browser/controller/input/textAreaWrapper';
import { Position } from 'vs/editor/common/core/position';
import { ViewLineRenderingData } from 'vs/editor/common/viewModel/viewModel';
// To run this test, open imeTester.html
......@@ -55,8 +54,12 @@ class SingleLineTestModel implements ISimpleModel {
return 1;
}
public getViewLineRenderingData(visibleRange: Range, lineNumber: number): ViewLineRenderingData {
return null;
public getPlainTextToCopy(ranges: Range[], enableEmptySelectionClipboard: boolean): string {
return '';
}
public getHTMLToCopy(ranges: Range[], enableEmptySelectionClipboard: boolean): string {
return '';
}
}
......@@ -101,7 +104,7 @@ function doCreateTest(strategy: TextAreaStrategy, description: string, inputStr:
let model = new SingleLineTestModel('some text');
let handler = new TextAreaHandler(browser, strategy, textAreaWrapper, model, null, () => { });
let handler = new TextAreaHandler(browser, strategy, textAreaWrapper, model, () => { });
input.onfocus = () => {
handler.setHasFocus(true);
......@@ -185,4 +188,4 @@ const TESTS = [
TESTS.forEach((t) => {
document.body.appendChild(doCreateTest(TextAreaStrategy.NVDA, t.description, t.in, t.out));
document.body.appendChild(doCreateTest(TextAreaStrategy.IENarrator, t.description, t.in, t.out));
});
});
\ No newline at end of file
......@@ -10,7 +10,6 @@ import { Position } from 'vs/editor/common/core/position';
import { Range } from 'vs/editor/common/core/range';
import { EndOfLinePreference } from 'vs/editor/common/editorCommon';
import { MockTextAreaWrapper } from 'vs/editor/test/common/mocks/mockTextAreaWrapper';
import { ViewLineRenderingData } from 'vs/editor/common/viewModel/viewModel';
suite('TextAreaState', () => {
......@@ -474,7 +473,11 @@ class SimpleModel implements ISimpleModel {
return this._lines.length;
}
public getViewLineRenderingData(visibleRange: Range, lineNumber: number): ViewLineRenderingData {
return null;
public getPlainTextToCopy(ranges: Range[], enableEmptySelectionClipboard: boolean): string {
return '';
}
public getHTMLToCopy(ranges: Range[], enableEmptySelectionClipboard: boolean): string {
return '';
}
}
......@@ -12,7 +12,6 @@ import 'vs/base/common/errors';
// Editor
import 'vs/editor/contrib/accessibility/browser/accessibility';
import 'vs/editor/contrib/defineKeybinding/browser/defineKeybinding';
import 'vs/editor/contrib/clipboard/electron-browser/richClipboard';
import 'vs/editor/contrib/inspectTMScopes/electron-browser/inspectTMScopes';
import 'vs/editor/contrib/selectionClipboard/electron-browser/selectionClipboard';
import 'vs/editor/browser/editor.all';
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册