未验证 提交 1e81535d 编写于 作者: A Alex Dima

Improve decoration tests

上级 11969fcf
......@@ -14,17 +14,17 @@ import { IModelDecorationOptions, IModelDecorationOverviewRulerOptions, Overview
import { IResourceEditorInput } from 'vs/platform/editor/common/editor';
import { IColorTheme, IThemeService, ThemeColor } from 'vs/platform/theme/common/themeService';
class RefCountedStyleSheet {
export class RefCountedStyleSheet {
private readonly _parent: CodeEditorServiceImpl;
private readonly _editorId: string;
public readonly styleSheet: HTMLStyleElement;
private readonly _styleSheet: HTMLStyleElement;
private _refCount: number;
constructor(parent: CodeEditorServiceImpl, editorId: string, styleSheet: HTMLStyleElement) {
this._parent = parent;
this._editorId = editorId;
this.styleSheet = styleSheet;
this._styleSheet = styleSheet;
this._refCount = 0;
}
......@@ -35,17 +35,26 @@ class RefCountedStyleSheet {
public unref(): void {
this._refCount--;
if (this._refCount === 0) {
this.styleSheet.parentNode?.removeChild(this.styleSheet);
this._styleSheet.parentNode?.removeChild(this._styleSheet);
this._parent._removeEditorStyleSheets(this._editorId);
}
}
public insertRule(rule: string, index?: number): void {
const sheet = <CSSStyleSheet>this._styleSheet.sheet;
sheet.insertRule(rule, index);
}
public removeRulesContainingSelector(ruleName: string): void {
dom.removeCSSRulesContainingSelector(ruleName, this._styleSheet);
}
}
class GlobalStyleSheet {
public readonly styleSheet: HTMLStyleElement;
export class GlobalStyleSheet {
private readonly _styleSheet: HTMLStyleElement;
constructor(styleSheet: HTMLStyleElement) {
this.styleSheet = styleSheet;
this._styleSheet = styleSheet;
}
public ref(): void {
......@@ -53,6 +62,15 @@ class GlobalStyleSheet {
public unref(): void {
}
public insertRule(rule: string, index?: number): void {
const sheet = <CSSStyleSheet>this._styleSheet.sheet;
sheet.insertRule(rule, index);
}
public removeRulesContainingSelector(ruleName: string): void {
dom.removeCSSRulesContainingSelector(ruleName, this._styleSheet);
}
}
export abstract class CodeEditorServiceImpl extends AbstractCodeEditorService {
......@@ -62,9 +80,9 @@ export abstract class CodeEditorServiceImpl extends AbstractCodeEditorService {
private readonly _editorStyleSheets = new Map<string, RefCountedStyleSheet>();
private readonly _themeService: IThemeService;
constructor(@IThemeService themeService: IThemeService, styleSheet: HTMLStyleElement | null = null) {
constructor(@IThemeService themeService: IThemeService, styleSheet: GlobalStyleSheet | null = null) {
super();
this._globalStyleSheet = styleSheet ? new GlobalStyleSheet(styleSheet) : null;
this._globalStyleSheet = styleSheet ? styleSheet : null;
this._themeService = themeService;
}
......@@ -100,7 +118,7 @@ export abstract class CodeEditorServiceImpl extends AbstractCodeEditorService {
if (!provider) {
const styleSheet = this._getOrCreateStyleSheet(editor);
const providerArgs: ProviderArguments = {
styleSheet: styleSheet.styleSheet,
styleSheet: styleSheet,
key: key,
parentTypeKey: parentTypeKey,
options: options || Object.create(null)
......@@ -188,7 +206,7 @@ class DecorationSubTypeOptionsProvider implements IModelDecorationOptionsProvide
}
interface ProviderArguments {
styleSheet: HTMLStyleElement;
styleSheet: GlobalStyleSheet | RefCountedStyleSheet;
key: string;
parentTypeKey?: string;
options: IDecorationRenderOptions;
......@@ -330,7 +348,7 @@ class DecorationCSSRules {
private readonly _providerArgs: ProviderArguments;
private _usesThemeColors: boolean;
public constructor(ruleType: ModelDecorationCSSRuleType, providerArgs: ProviderArguments, themeService: IThemeService) {
constructor(ruleType: ModelDecorationCSSRuleType, providerArgs: ProviderArguments, themeService: IThemeService) {
this._theme = themeService.getColorTheme();
this._ruleType = ruleType;
this._providerArgs = providerArgs;
......@@ -414,7 +432,7 @@ class DecorationCSSRules {
default:
throw new Error('Unknown rule type: ' + this._ruleType);
}
const sheet = <CSSStyleSheet>this._providerArgs.styleSheet.sheet;
const sheet = this._providerArgs.styleSheet;
let hasContent = false;
if (unthemedCSS.length > 0) {
......@@ -433,7 +451,7 @@ class DecorationCSSRules {
}
private _removeCSS(): void {
dom.removeCSSRulesContainingSelector(this._unThemedSelector, this._providerArgs.styleSheet);
this._providerArgs.styleSheet.removeRulesContainingSelector(this._unThemedSelector);
}
/**
......
......@@ -4,18 +4,17 @@
*--------------------------------------------------------------------------------------------*/
import * as assert from 'assert';
import * as dom from 'vs/base/browser/dom';
import * as platform from 'vs/base/common/platform';
import { URI } from 'vs/base/common/uri';
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
import { CodeEditorServiceImpl } from 'vs/editor/browser/services/codeEditorServiceImpl';
import { CodeEditorServiceImpl, GlobalStyleSheet } from 'vs/editor/browser/services/codeEditorServiceImpl';
import { IDecorationRenderOptions } from 'vs/editor/common/editorCommon';
import { IResourceEditorInput } from 'vs/platform/editor/common/editor';
import { TestColorTheme, TestThemeService } from 'vs/platform/theme/test/common/testThemeService';
const themeServiceMock = new TestThemeService();
export class TestCodeEditorServiceImpl extends CodeEditorServiceImpl {
class TestCodeEditorServiceImpl extends CodeEditorServiceImpl {
getActiveCodeEditor(): ICodeEditor | null {
return null;
}
......@@ -25,6 +24,32 @@ export class TestCodeEditorServiceImpl extends CodeEditorServiceImpl {
}
}
class TestGlobalStyleSheet extends GlobalStyleSheet {
public rules: string[] = [];
constructor() {
super(null!);
}
public insertRule(rule: string, index?: number): void {
this.rules.unshift(rule);
}
public removeRulesContainingSelector(ruleName: string): void {
for (let i = 0; i < this.rules.length; i++) {
if (this.rules[i].indexOf(ruleName) >= 0) {
this.rules.splice(i, 1);
i--;
}
}
}
public read(): string {
return this.rules.join('\n');
}
}
suite('Decoration Render Options', () => {
let options: IDecorationRenderOptions = {
gutterIconPath: URI.parse('https://github.com/Microsoft/vscode/blob/master/resources/linux/code.png'),
......@@ -45,59 +70,45 @@ suite('Decoration Render Options', () => {
assert.throws(() => s.resolveDecorationOptions('example', false));
});
function readStyleSheet(styleSheet: HTMLStyleElement): string {
if ((<any>styleSheet.sheet).rules) {
return Array.prototype.map.call((<any>styleSheet.sheet).rules, (r: { cssText: string }) => r.cssText).join('\n');
}
return styleSheet.sheet!.toString();
function readStyleSheet(styleSheet: TestGlobalStyleSheet): string {
return styleSheet.read();
}
test('css properties', () => {
let styleSheet = dom.createStyleSheet();
let s = new TestCodeEditorServiceImpl(themeServiceMock, styleSheet);
const styleSheet = new TestGlobalStyleSheet();
const s = new TestCodeEditorServiceImpl(themeServiceMock, styleSheet);
s.registerDecorationType('example', options);
let sheet = readStyleSheet(styleSheet);
assert(
sheet.indexOf('background: url(\'https://github.com/Microsoft/vscode/blob/master/resources/linux/code.png\') center center no-repeat;') > 0
|| sheet.indexOf('background: url("https://github.com/Microsoft/vscode/blob/master/resources/linux/code.png") center center / contain no-repeat;') > 0
|| sheet.indexOf('background-image: url("https://github.com/Microsoft/vscode/blob/master/resources/linux/code.png"); background-size: contain; background-position: center center; background-repeat: no-repeat no-repeat;') > 0
);
assert(sheet.indexOf('border-color: yellow;') > 0);
assert(sheet.indexOf('background-color: red;') > 0);
const sheet = readStyleSheet(styleSheet);
assert(sheet.indexOf(`{background:url('https://github.com/Microsoft/vscode/blob/master/resources/linux/code.png') center center no-repeat;background-size:contain;}`) >= 0);
assert(sheet.indexOf(`{background-color:red;border-color:yellow;box-sizing: border-box;}`) >= 0);
});
test('theme color', () => {
let options: IDecorationRenderOptions = {
const options: IDecorationRenderOptions = {
backgroundColor: { id: 'editorBackground' },
borderColor: { id: 'editorBorder' },
};
let colors: { [key: string]: string } = {
editorBackground: '#FF0000'
};
let styleSheet = dom.createStyleSheet();
let themeService = new TestThemeService(new TestColorTheme(colors));
let s = new TestCodeEditorServiceImpl(themeService, styleSheet);
const styleSheet = new TestGlobalStyleSheet();
const themeService = new TestThemeService(new TestColorTheme({
editorBackground: '#FF0000'
}));
const s = new TestCodeEditorServiceImpl(themeService, styleSheet);
s.registerDecorationType('example', options);
let sheet = readStyleSheet(styleSheet);
assert.equal(sheet, '.monaco-editor .ced-example-0 { background-color: rgb(255, 0, 0); border-color: transparent; box-sizing: border-box; }');
assert.equal(readStyleSheet(styleSheet), '.monaco-editor .ced-example-0 {background-color:#ff0000;border-color:transparent;box-sizing: border-box;}');
colors = {
themeService.setTheme(new TestColorTheme({
editorBackground: '#EE0000',
editorBorder: '#00FFFF'
};
themeService.setTheme(new TestColorTheme(colors));
sheet = readStyleSheet(styleSheet);
assert.equal(sheet, '.monaco-editor .ced-example-0 { background-color: rgb(238, 0, 0); border-color: rgb(0, 255, 255); box-sizing: border-box; }');
}));
assert.equal(readStyleSheet(styleSheet), '.monaco-editor .ced-example-0 {background-color:#ee0000;border-color:#00ffff;box-sizing: border-box;}');
s.removeDecorationType('example');
sheet = readStyleSheet(styleSheet);
assert.equal(sheet, '');
assert.equal(readStyleSheet(styleSheet), '');
});
test('theme overrides', () => {
let options: IDecorationRenderOptions = {
const options: IDecorationRenderOptions = {
color: { id: 'editorBackground' },
light: {
color: '#FF00FF'
......@@ -109,86 +120,70 @@ suite('Decoration Render Options', () => {
}
}
};
let colors: { [key: string]: string } = {
const styleSheet = new TestGlobalStyleSheet();
const themeService = new TestThemeService(new TestColorTheme({
editorBackground: '#FF0000',
infoForeground: '#444444'
};
let styleSheet = dom.createStyleSheet();
let themeService = new TestThemeService(new TestColorTheme(colors));
let s = new TestCodeEditorServiceImpl(themeService, styleSheet);
}));
const s = new TestCodeEditorServiceImpl(themeService, styleSheet);
s.registerDecorationType('example', options);
let sheet = readStyleSheet(styleSheet);
let expected =
'.vs-dark.monaco-editor .ced-example-4::after, .hc-black.monaco-editor .ced-example-4::after { color: rgb(68, 68, 68) !important; }\n' +
'.vs-dark.monaco-editor .ced-example-1, .hc-black.monaco-editor .ced-example-1 { color: rgb(0, 0, 0) !important; }\n' +
'.vs.monaco-editor .ced-example-1 { color: rgb(255, 0, 255) !important; }\n' +
'.monaco-editor .ced-example-1 { color: rgb(255, 0, 0) !important; }';
assert.equal(sheet, expected);
const expected = [
'.vs-dark.monaco-editor .ced-example-4::after, .hc-black.monaco-editor .ced-example-4::after {color:#444444 !important;}',
'.vs-dark.monaco-editor .ced-example-1, .hc-black.monaco-editor .ced-example-1 {color:#000000 !important;}',
'.vs.monaco-editor .ced-example-1 {color:#FF00FF !important;}',
'.monaco-editor .ced-example-1 {color:#ff0000 !important;}'
].join('\n');
assert.equal(readStyleSheet(styleSheet), expected);
s.removeDecorationType('example');
sheet = readStyleSheet(styleSheet);
assert.equal(sheet, '');
assert.equal(readStyleSheet(styleSheet), '');
});
test('css properties, gutterIconPaths', () => {
let styleSheet = dom.createStyleSheet();
// unix file path (used as string)
let s = new TestCodeEditorServiceImpl(themeServiceMock, styleSheet);
s.registerDecorationType('example', { gutterIconPath: URI.file('/Users/foo/bar.png') });
let sheet = readStyleSheet(styleSheet);//.innerHTML || styleSheet.sheet.toString();
assert(
sheet.indexOf('background: url(\'file:///Users/foo/bar.png\') center center no-repeat;') > 0
|| sheet.indexOf('background: url("file:///Users/foo/bar.png") center center no-repeat;') > 0
|| sheet.indexOf('background-image: url("file:///Users/foo/bar.png"); background-position: center center; background-repeat: no-repeat no-repeat;') > 0
);
const styleSheet = new TestGlobalStyleSheet();
const s = new TestCodeEditorServiceImpl(themeServiceMock, styleSheet);
// URI, only minimal encoding
s.registerDecorationType('example', { gutterIconPath: URI.parse('data:image/svg+xml;base64,PHN2ZyB4b+') });
assert(readStyleSheet(styleSheet).indexOf(`{background:url('data:image/svg+xml;base64,PHN2ZyB4b+') center center no-repeat;}`) > 0);
s.removeDecorationType('example');
// windows file path (used as string)
if (platform.isWindows) {
s = new TestCodeEditorServiceImpl(themeServiceMock, styleSheet);
// windows file path (used as string)
s.registerDecorationType('example', { gutterIconPath: URI.file('c:\\files\\miles\\more.png') });
sheet = readStyleSheet(styleSheet);
const sheet1 = readStyleSheet(styleSheet);
assert(
sheet.indexOf('background: url(\'file:///c%3A/files/miles/more.png\') center center no-repeat;') > 0
|| sheet.indexOf('background: url("file:///c%3A/files/miles/more.png") center center no-repeat;') > 0
|| sheet.indexOf('background: url("file:///c:/files/miles/more.png") center center no-repeat;') > 0
|| sheet.indexOf('background-image: url("file:///c:/files/miles/more.png"); background-position: center center; background-repeat: no-repeat no-repeat;') > 0
sheet1.indexOf('background: url(\'file:///c%3A/files/miles/more.png\') center center no-repeat;') > 0
|| sheet1.indexOf('background: url("file:///c%3A/files/miles/more.png") center center no-repeat;') > 0
|| sheet1.indexOf('background: url("file:///c:/files/miles/more.png") center center no-repeat;') > 0
|| sheet1.indexOf('background-image: url("file:///c:/files/miles/more.png"); background-position: center center; background-repeat: no-repeat no-repeat;') > 0
);
s.removeDecorationType('example');
}
// URI, only minimal encoding
s = new TestCodeEditorServiceImpl(themeServiceMock, styleSheet);
s.registerDecorationType('example', { gutterIconPath: URI.parse('data:image/svg+xml;base64,PHN2ZyB4b+') });
sheet = readStyleSheet(styleSheet);
assert(
sheet.indexOf('background: url(\'data:image/svg+xml;base64,PHN2ZyB4b+\') center center no-repeat;') > 0
|| sheet.indexOf('background: url("data:image/svg+xml;base64,PHN2ZyB4b+") center center no-repeat;') > 0
|| sheet.indexOf('background-image: url("data:image/svg+xml;base64,PHN2ZyB4b+"); background-position: center center; background-repeat: no-repeat no-repeat;') > 0
);
s.removeDecorationType('example');
// single quote must always be escaped/encoded
s.registerDecorationType('example', { gutterIconPath: URI.file('c:\\files\\foo\\b\'ar.png') });
const sheet2 = readStyleSheet(styleSheet);
assert(
sheet2.indexOf('background: url(\'file:///Users/foo/b%27ar.png\') center center no-repeat;') > 0
|| sheet2.indexOf('background: url("file:///Users/foo/b%27ar.png") center center no-repeat;') > 0
|| sheet2.indexOf('background-image: url("file:///Users/foo/b%27ar.png"); background-position: center center; background-repeat: no-repeat no-repeat;') > 0
);
s.removeDecorationType('example');
} else {
// unix file path (used as string)
s.registerDecorationType('example', { gutterIconPath: URI.file('/Users/foo/bar.png') });
assert(readStyleSheet(styleSheet).indexOf(`{background:url('file:///Users/foo/bar.png') center center no-repeat;}`) > 0);
s.removeDecorationType('example');
// single quote must always be escaped/encoded
s = new TestCodeEditorServiceImpl(themeServiceMock, styleSheet);
s.registerDecorationType('example', { gutterIconPath: URI.file('/Users/foo/b\'ar.png') });
sheet = readStyleSheet(styleSheet);
assert(
sheet.indexOf('background: url(\'file:///Users/foo/b%27ar.png\') center center no-repeat;') > 0
|| sheet.indexOf('background: url("file:///Users/foo/b%27ar.png") center center no-repeat;') > 0
|| sheet.indexOf('background-image: url("file:///Users/foo/b%27ar.png"); background-position: center center; background-repeat: no-repeat no-repeat;') > 0
);
s.removeDecorationType('example');
// single quote must always be escaped/encoded
s.registerDecorationType('example', { gutterIconPath: URI.file('/Users/foo/b\'ar.png') });
assert(readStyleSheet(styleSheet).indexOf(`{background:url('file:///Users/foo/b%27ar.png') center center no-repeat;}`) > 0);
s.removeDecorationType('example');
}
s = new TestCodeEditorServiceImpl(themeServiceMock, styleSheet);
s.registerDecorationType('example', { gutterIconPath: URI.parse('http://test/pa\'th') });
sheet = readStyleSheet(styleSheet);
assert(
sheet.indexOf('background: url(\'http://test/pa%27th\') center center no-repeat;') > 0
|| sheet.indexOf('background: url("http://test/pa%27th") center center no-repeat;') > 0
|| sheet.indexOf('background-image: url("http://test/pa%27th"); background-position: center center; background-repeat: no-repeat no-repeat;') > 0
);
assert(readStyleSheet(styleSheet).indexOf(`{background:url('http://test/pa%27th') center center no-repeat;}`) > 0);
s.removeDecorationType('example');
});
});
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册