Merge branch 'master' into smoketest

This commit is contained in:
Joao Moreno
2018-04-05 13:55:37 +02:00
175 changed files with 3090 additions and 2018 deletions
+2 -2
View File
@@ -1,12 +1,12 @@
[
{
"name": "ms-vscode.node-debug",
"version": "1.22.13",
"version": "1.23.1",
"repo": "https://github.com/Microsoft/vscode-node-debug"
},
{
"name": "ms-vscode.node-debug2",
"version": "1.22.6",
"version": "1.23.0",
"repo": "https://github.com/Microsoft/vscode-node-debug2"
}
]
@@ -5,15 +5,14 @@
'use strict';
import {
createConnection, IConnection, TextDocuments, InitializeParams, InitializeResult, ServerCapabilities,
ConfigurationRequest, WorkspaceFolder, DocumentColorRequest, ColorPresentationRequest
createConnection, IConnection, TextDocuments, InitializeParams, InitializeResult, ServerCapabilities, ConfigurationRequest, WorkspaceFolder
} from 'vscode-languageserver';
import { TextDocument, CompletionList } from 'vscode-languageserver-types';
import { getCSSLanguageService, getSCSSLanguageService, getLESSLanguageService, LanguageSettings, LanguageService, Stylesheet } from 'vscode-css-languageservice';
import { getLanguageModelCache } from './languageModelCache';
import { formatError, runSafe } from './utils/errors';
import { formatError, runSafe } from './utils/runner';
import URI from 'vscode-uri';
import { getPathCompletionParticipant } from './pathCompletion';
import { FoldingProviderServerCapabilities, FoldingRangesRequest } from 'vscode-languageserver-protocol-foldingprovider';
@@ -182,7 +181,7 @@ function validateTextDocument(textDocument: TextDocument): void {
});
}
connection.onCompletion(textDocumentPosition => {
connection.onCompletion((textDocumentPosition, token) => {
return runSafe(() => {
let document = documents.get(textDocumentPosition.textDocument.uri);
const cssLS = getLanguageService(document);
@@ -196,58 +195,58 @@ connection.onCompletion(textDocumentPosition => {
isIncomplete: result.isIncomplete,
items: [...pathCompletionList.items, ...result.items]
};
}, null, `Error while computing completions for ${textDocumentPosition.textDocument.uri}`);
}, null, `Error while computing completions for ${textDocumentPosition.textDocument.uri}`, token);
});
connection.onHover(textDocumentPosition => {
connection.onHover((textDocumentPosition, token) => {
return runSafe(() => {
let document = documents.get(textDocumentPosition.textDocument.uri);
let styleSheet = stylesheets.get(document);
return getLanguageService(document).doHover(document, textDocumentPosition.position, styleSheet)!; /* TODO: remove ! once LS has null annotations */
}, null, `Error while computing hover for ${textDocumentPosition.textDocument.uri}`);
return getLanguageService(document).doHover(document, textDocumentPosition.position, styleSheet);
}, null, `Error while computing hover for ${textDocumentPosition.textDocument.uri}`, token);
});
connection.onDocumentSymbol(documentSymbolParams => {
connection.onDocumentSymbol((documentSymbolParams, token) => {
return runSafe(() => {
let document = documents.get(documentSymbolParams.textDocument.uri);
let stylesheet = stylesheets.get(document);
return getLanguageService(document).findDocumentSymbols(document, stylesheet);
}, [], `Error while computing document symbols for ${documentSymbolParams.textDocument.uri}`);
}, [], `Error while computing document symbols for ${documentSymbolParams.textDocument.uri}`, token);
});
connection.onDefinition(documentSymbolParams => {
connection.onDefinition((documentSymbolParams, token) => {
return runSafe(() => {
let document = documents.get(documentSymbolParams.textDocument.uri);
let stylesheet = stylesheets.get(document);
return getLanguageService(document).findDefinition(document, documentSymbolParams.position, stylesheet);
}, null, `Error while computing definitions for ${documentSymbolParams.textDocument.uri}`);
}, null, `Error while computing definitions for ${documentSymbolParams.textDocument.uri}`, token);
});
connection.onDocumentHighlight(documentSymbolParams => {
connection.onDocumentHighlight((documentSymbolParams, token) => {
return runSafe(() => {
let document = documents.get(documentSymbolParams.textDocument.uri);
let stylesheet = stylesheets.get(document);
return getLanguageService(document).findDocumentHighlights(document, documentSymbolParams.position, stylesheet);
}, [], `Error while computing document highlights for ${documentSymbolParams.textDocument.uri}`);
}, [], `Error while computing document highlights for ${documentSymbolParams.textDocument.uri}`, token);
});
connection.onReferences(referenceParams => {
connection.onReferences((referenceParams, token) => {
return runSafe(() => {
let document = documents.get(referenceParams.textDocument.uri);
let stylesheet = stylesheets.get(document);
return getLanguageService(document).findReferences(document, referenceParams.position, stylesheet);
}, [], `Error while computing references for ${referenceParams.textDocument.uri}`);
}, [], `Error while computing references for ${referenceParams.textDocument.uri}`, token);
});
connection.onCodeAction(codeActionParams => {
connection.onCodeAction((codeActionParams, token) => {
return runSafe(() => {
let document = documents.get(codeActionParams.textDocument.uri);
let stylesheet = stylesheets.get(document);
return getLanguageService(document).doCodeActions(document, codeActionParams.range, codeActionParams.context, stylesheet);
}, [], `Error while computing code actions for ${codeActionParams.textDocument.uri}`);
}, [], `Error while computing code actions for ${codeActionParams.textDocument.uri}`, token);
});
connection.onRequest(DocumentColorRequest.type, params => {
connection.onDocumentColor((params, token) => {
return runSafe(() => {
let document = documents.get(params.textDocument.uri);
if (document) {
@@ -255,10 +254,10 @@ connection.onRequest(DocumentColorRequest.type, params => {
return getLanguageService(document).findDocumentColors(document, stylesheet);
}
return [];
}, [], `Error while computing document colors for ${params.textDocument.uri}`);
}, [], `Error while computing document colors for ${params.textDocument.uri}`, token);
});
connection.onRequest(ColorPresentationRequest.type, params => {
connection.onColorPresentation((params, token) => {
return runSafe(() => {
let document = documents.get(params.textDocument.uri);
if (document) {
@@ -266,15 +265,15 @@ connection.onRequest(ColorPresentationRequest.type, params => {
return getLanguageService(document).getColorPresentations(document, stylesheet, params.color, params.range);
}
return [];
}, [], `Error while computing color presentations for ${params.textDocument.uri}`);
}, [], `Error while computing color presentations for ${params.textDocument.uri}`, token);
});
connection.onRenameRequest(renameParameters => {
connection.onRenameRequest((renameParameters, token) => {
return runSafe(() => {
let document = documents.get(renameParameters.textDocument.uri);
let stylesheet = stylesheets.get(document);
return getLanguageService(document).doRename(document, renameParameters.position, renameParameters.newName, stylesheet);
}, null, `Error while computing renames for ${renameParameters.textDocument.uri}`);
}, null, `Error while computing renames for ${renameParameters.textDocument.uri}`, token);
});
connection.onRequest(FoldingRangesRequest.type, (params, token) => {
@@ -282,7 +281,7 @@ connection.onRequest(FoldingRangesRequest.type, (params, token) => {
let document = documents.get(params.textDocument.uri);
let stylesheet = stylesheets.get(document);
return getLanguageService(document).findFoldingRegions(document, stylesheet);
}, null, `Error while computing folding ranges for ${params.textDocument.uri}`);
}, null, `Error while computing folding ranges for ${params.textDocument.uri}`, token);
});
// Listen on the connection
@@ -48,7 +48,7 @@ suite('Completions', () => {
cssLanguageService.setCompletionParticipants([getPathCompletionParticipant(document, workspaceFolders, participantResult)]);
const stylesheet = cssLanguageService.parseStylesheet(document);
let list = cssLanguageService.doComplete!(document, position, stylesheet);
let list = cssLanguageService.doComplete(document, position, stylesheet)!;
list.items = list.items.concat(participantResult.items);
if (expected.count) {
@@ -15,7 +15,7 @@ suite('CSS Emmet Support', () => {
const cssLanguageService = getCSSLanguageService();
const scssLanguageService = getSCSSLanguageService();
function assertCompletions(syntax: string, value: string, expectedProposal: string, expectedProposalDoc: string): void {
function assertCompletions(syntax: string, value: string, expectedProposal: string | null, expectedProposalDoc: string | null): void {
const offset = value.indexOf('|');
value = value.substr(0, offset) + value.substr(offset + 1);
@@ -23,12 +23,12 @@ suite('CSS Emmet Support', () => {
const position = document.positionAt(offset);
const emmetCompletionList: CompletionList = {
isIncomplete: true,
items: undefined
items: []
};
const languageService = syntax === 'scss' ? scssLanguageService : cssLanguageService;
languageService.setCompletionParticipants([getEmmetCompletionParticipants(document, position, document.languageId, {}, emmetCompletionList)]);
const stylesheet = languageService.parseStylesheet(document);
const list = languageService.doComplete!(document, position, stylesheet);
const list = languageService.doComplete(document, position, stylesheet);
assert.ok(list);
assert.ok(emmetCompletionList);
@@ -43,7 +43,7 @@ suite('CSS Emmet Support', () => {
}
}
test('Css Emmet Completions', function (): any {
test('Css Emmet Completions', function (this: any): any {
this.skip(); // disabled again (see #29113)
assertCompletions('css', '.foo { display: none; m10| }', 'margin: 10px;', 'margin: 10px;');
@@ -56,7 +56,7 @@ suite('CSS Emmet Support', () => {
assertCompletions('css', '.foo { display: none; -m-m10| }', 'margin: 10px;', '-moz-margin: 10px;\nmargin: 10px;');
});
test('Scss Emmet Completions', function (): any {
test('Scss Emmet Completions', function (this: any): any {
this.skip(); // disabled again (see #29113)
assertCompletions('scss', '.foo { display: none; .bar { m10| } }', 'margin: 10px;', 'margin: 10px;');
@@ -1,33 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
export function formatError(message: string, err: any): string {
if (err instanceof Error) {
let error = <Error>err;
return `${message}: ${error.message}\n${error.stack}`;
} else if (typeof err === 'string') {
return `${message}: ${err}`;
} else if (err) {
return `${message}: ${err.toString()}`;
}
return message;
}
export function runSafe<T>(func: () => Thenable<T> | T, errorVal: T, errorMessage: string): Thenable<T> | T {
try {
let t = func();
if (t instanceof Promise) {
return t.then(void 0, e => {
console.error(formatError(errorMessage, e));
return errorVal;
});
}
return t;
} catch (e) {
console.error(formatError(errorMessage, e));
return errorVal;
}
}
@@ -0,0 +1,47 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import { ResponseError, ErrorCodes, CancellationToken } from 'vscode-languageserver';
export function formatError(message: string, err: any): string {
if (err instanceof Error) {
let error = <Error>err;
return `${message}: ${error.message}\n${error.stack}`;
} else if (typeof err === 'string') {
return `${message}: ${err}`;
} else if (err) {
return `${message}: ${err.toString()}`;
}
return message;
}
export function runSafe<T, E>(func: () => T, errorVal: T, errorMessage: string, token: CancellationToken): Thenable<T | ResponseError<E>> {
return new Promise<T | ResponseError<E>>((resolve, reject) => {
setImmediate(() => {
if (token.isCancellationRequested) {
resolve(cancelValue());
} else {
try {
let result = func();
if (token.isCancellationRequested) {
resolve(cancelValue());
return;
} else {
resolve(result);
}
} catch (e) {
console.error(formatError(errorMessage, e));
resolve(errorVal);
}
}
});
});
}
function cancelValue<E>() {
return new ResponseError<E>(ErrorCodes.RequestCancelled, 'Request cancelled');
}
@@ -6,7 +6,8 @@
"noUnusedLocals": true,
"lib": [
"es5", "es2015.promise"
]
],
"strict": true
},
"include": [
"src/**/*"
@@ -22,7 +22,8 @@
"onCommand:markdown.showPreviewToSide",
"onCommand:markdown.showLockedPreviewToSide",
"onCommand:markdown.showSource",
"onCommand:markdown.showPreviewSecuritySelector"
"onCommand:markdown.showPreviewSecuritySelector",
"onView:markdown.preview"
],
"contributes": {
"commands": [
@@ -18,37 +18,85 @@ const localize = nls.loadMessageBundle();
export class MarkdownPreview {
public static previewViewType = 'markdown.preview';
public static viewType = 'markdown.preview';
private readonly webview: vscode.Webview;
private throttleTimer: any;
private initialLine: number | undefined = undefined;
private line: number | undefined = undefined;
private readonly disposables: vscode.Disposable[] = [];
private firstUpdate = true;
private currentVersion?: { resource: vscode.Uri, version: number };
private forceUpdate = false;
private isScrolling = false;
constructor(
private _resource: vscode.Uri,
public static revive(
webview: vscode.Webview,
state: any,
contentProvider: MarkdownContentProvider,
previewConfigurations: MarkdownPreviewConfigurationManager,
logger: Logger,
topmostLineMonitor: MarkdownFileTopmostLineMonitor
): MarkdownPreview {
const resource = vscode.Uri.parse(state.resource);
const locked = state.locked;
const line = state.line;
const preview = new MarkdownPreview(
webview,
resource,
locked,
contentProvider,
previewConfigurations,
logger,
topmostLineMonitor);
if (!isNaN(line)) {
preview.line = line;
}
return preview;
}
public static create(
resource: vscode.Uri,
previewColumn: vscode.ViewColumn,
public locked: boolean,
private readonly contentProvider: MarkdownContentProvider,
private readonly previewConfigurations: MarkdownPreviewConfigurationManager,
private readonly logger: Logger,
locked: boolean,
contentProvider: MarkdownContentProvider,
previewConfigurations: MarkdownPreviewConfigurationManager,
logger: Logger,
topmostLineMonitor: MarkdownFileTopmostLineMonitor,
private readonly contributions: MarkdownContributions
) {
this.webview = vscode.window.createWebview(
MarkdownPreview.previewViewType,
this.getPreviewTitle(this._resource),
contributions: MarkdownContributions
): MarkdownPreview {
const webview = vscode.window.createWebview(
MarkdownPreview.viewType,
MarkdownPreview.getPreviewTitle(resource, locked),
previewColumn, {
enableScripts: true,
enableCommandUris: true,
enableFindWidget: true,
localResourceRoots: this.getLocalResourceRoots(_resource)
localResourceRoots: MarkdownPreview.getLocalResourceRoots(resource, contributions)
});
return new MarkdownPreview(
webview,
resource,
locked,
contentProvider,
previewConfigurations,
logger,
topmostLineMonitor);
}
private constructor(
webview: vscode.Webview,
private _resource: vscode.Uri,
public locked: boolean,
private readonly contentProvider: MarkdownContentProvider,
private readonly previewConfigurations: MarkdownPreviewConfigurationManager,
private readonly logger: Logger,
topmostLineMonitor: MarkdownFileTopmostLineMonitor
) {
this.webview = webview;
this.webview.onDidDispose(() => {
this.dispose();
}, null, this.disposables);
@@ -111,6 +159,14 @@ export class MarkdownPreview {
return this._resource;
}
public get state() {
return {
resource: this.resource.toString(),
locked: this.locked,
line: this.line
};
}
public dispose() {
this._onDisposeEmitter.fire();
@@ -124,9 +180,7 @@ export class MarkdownPreview {
public update(resource: vscode.Uri) {
const editor = vscode.window.activeTextEditor;
if (editor && editor.document.uri.fsPath === resource.fsPath) {
this.initialLine = getVisibleLine(editor);
} else {
this.initialLine = undefined;
this.line = getVisibleLine(editor);
}
// If we have changed resources, cancel any pending updates
@@ -169,6 +223,10 @@ export class MarkdownPreview {
return this._resource.fsPath === resource.fsPath;
}
public isWebviewOf(webview: vscode.Webview): boolean {
return this.webview === webview;
}
public matchesResource(
otherResource: vscode.Uri,
otherViewColumn: vscode.ViewColumn | undefined,
@@ -195,11 +253,11 @@ export class MarkdownPreview {
public toggleLock() {
this.locked = !this.locked;
this.webview.title = this.getPreviewTitle(this._resource);
this.webview.title = MarkdownPreview.getPreviewTitle(this._resource, this.locked);
}
private getPreviewTitle(resource: vscode.Uri): string {
return this.locked
private static getPreviewTitle(resource: vscode.Uri, locked: boolean): string {
return locked
? localize('lockedPreviewTitle', '[Preview] {0}', path.basename(resource.fsPath))
: localize('previewTitle', 'Preview {0}', path.basename(resource.fsPath));
}
@@ -216,7 +274,7 @@ export class MarkdownPreview {
if (typeof topLine === 'number') {
this.logger.log('updateForView', { markdownFile: resource });
this.initialLine = topLine;
this.line = topLine;
this.webview.postMessage({
type: 'updateView',
line: topLine,
@@ -233,25 +291,28 @@ export class MarkdownPreview {
const document = await vscode.workspace.openTextDocument(resource);
if (!this.forceUpdate && this.currentVersion && this.currentVersion.resource.fsPath === resource.fsPath && this.currentVersion.version === document.version) {
if (this.initialLine) {
this.updateForView(resource, this.initialLine);
if (this.line) {
this.updateForView(resource, this.line);
}
return;
}
this.forceUpdate = false;
this.currentVersion = { resource, version: document.version };
this.contentProvider.provideTextDocumentContent(document, this.previewConfigurations, this.initialLine)
this.contentProvider.provideTextDocumentContent(document, this.previewConfigurations, this.line)
.then(content => {
if (this._resource === resource) {
this.webview.title = this.getPreviewTitle(this._resource);
this.webview.title = MarkdownPreview.getPreviewTitle(this._resource, this.locked);
this.webview.html = content;
}
});
}
private getLocalResourceRoots(resource: vscode.Uri): vscode.Uri[] {
const baseRoots = this.contributions.previewResourceRoots;
private static getLocalResourceRoots(
resource: vscode.Uri,
contributions: MarkdownContributions
): vscode.Uri[] {
const baseRoots = contributions.previewResourceRoots;
const folder = vscode.workspace.getWorkspaceFolder(resource);
if (folder) {
@@ -266,6 +327,7 @@ export class MarkdownPreview {
}
private onDidScrollPreview(line: number) {
this.line = line;
for (const editor of vscode.window.visibleTextEditors) {
if (!this.isPreviewOf(editor.document.uri)) {
continue;
@@ -14,7 +14,7 @@ import { isMarkdownFile } from '../util/file';
import { MarkdownPreviewConfigurationManager } from './previewConfig';
import { MarkdownContributions } from '../markdownExtensions';
export class MarkdownPreviewManager {
export class MarkdownPreviewManager implements vscode.WebviewSerializer {
private static readonly markdownPreviewActiveContextKey = 'markdownPreviewFocus';
private readonly topmostLineMonitor = new MarkdownFileTopmostLineMonitor();
@@ -29,15 +29,14 @@ export class MarkdownPreviewManager {
private readonly contributions: MarkdownContributions
) {
vscode.window.onDidChangeActiveTextEditor(editor => {
if (editor) {
if (isMarkdownFile(editor.document)) {
for (const preview of this.previews.filter(preview => !preview.locked)) {
preview.update(editor.document.uri);
}
if (editor && isMarkdownFile(editor.document)) {
for (const preview of this.previews.filter(preview => !preview.locked)) {
preview.update(editor.document.uri);
}
}
}, null, this.disposables);
this.disposables.push(vscode.window.registerWebviewSerializer(MarkdownPreview.viewType, this));
}
public dispose(): void {
@@ -66,7 +65,6 @@ export class MarkdownPreviewManager {
preview.reveal(previewSettings.previewColumn);
} else {
preview = this.createNewPreview(resource, previewSettings);
this.previews.push(preview);
}
preview.update(resource);
@@ -90,6 +88,30 @@ export class MarkdownPreviewManager {
}
}
public async deserializeWebview(
webview: vscode.Webview,
state: any
): Promise<boolean> {
const preview = MarkdownPreview.revive(
webview,
state,
this.contentProvider,
this.previewConfigurations,
this.logger,
this.topmostLineMonitor);
this.registerPreview(preview);
preview.refresh();
return true;
}
public async serializeWebview(
webview: vscode.Webview,
): Promise<any> {
const preview = this.previews.find(preview => preview.isWebviewOf(webview));
return preview ? preview.state : undefined;
}
private getExistingPreview(
resource: vscode.Uri,
previewSettings: PreviewSettings
@@ -101,8 +123,8 @@ export class MarkdownPreviewManager {
private createNewPreview(
resource: vscode.Uri,
previewSettings: PreviewSettings
) {
const preview = new MarkdownPreview(
): MarkdownPreview {
const preview = MarkdownPreview.create(
resource,
previewSettings.previewColumn,
previewSettings.locked,
@@ -112,6 +134,14 @@ export class MarkdownPreviewManager {
this.topmostLineMonitor,
this.contributions);
return this.registerPreview(preview);
}
private registerPreview(
preview: MarkdownPreview
): MarkdownPreview {
this.previews.push(preview);
preview.onDispose(() => {
const existing = this.previews.indexOf(preview!);
if (existing >= 0) {
+1 -1
View File
@@ -1,4 +1,4 @@
{
"displayName": "PHP Language Features",
"displayName": "PHP Language Basics",
"description": "Provides syntax highlighting and bracket matching for PHP files."
}
+1 -7
View File
@@ -5,10 +5,4 @@
.monaco-builder-hidden {
display: none !important;
visibility: hidden !important;
}
.monaco-builder-visible {
display: inherit;
visibility: visible;
}
}
+32
View File
@@ -590,6 +590,36 @@ export interface IDomNodePagePosition {
height: number;
}
export function size(element: HTMLElement, width: number, height: number): void {
if (typeof width === 'number') {
element.style.width = `${width}px`;
}
if (typeof height === 'number') {
element.style.height = `${height}px`;
}
}
export function position(element: HTMLElement, top: number, right?: number, bottom?: number, left?: number, position: string = 'absolute'): void {
if (typeof top === 'number') {
element.style.top = `${top}px`;
}
if (typeof right === 'number') {
element.style.right = `${right}px`;
}
if (typeof bottom === 'number') {
element.style.bottom = `${bottom}px`;
}
if (typeof left === 'number') {
element.style.left = `${left}px`;
}
element.style.position = position;
}
/**
* Returns the position of a dom node relative to the entire page.
*/
@@ -994,12 +1024,14 @@ export function join(nodes: Node[], separator: Node | string): Node[] {
export function show(...elements: HTMLElement[]): void {
for (let element of elements) {
element.style.display = '';
element.removeAttribute('aria-hidden');
}
}
export function hide(...elements: HTMLElement[]): void {
for (let element of elements) {
element.style.display = 'none';
element.setAttribute('aria-hidden', 'true');
}
}
-1
View File
@@ -636,7 +636,6 @@ suite('Builder', () => {
assert(!b.isHidden());
b.hide();
assert(b.isHidden());
assert(!b.hasClass('monaco-builder-visible'));
b.show();
b.hide();
assert(b.hasClass('monaco-builder-hidden'));
@@ -375,33 +375,32 @@ export class IssueReporter extends Disposable {
});
this.addEventListener('disableExtensions', 'keydown', (e: KeyboardEvent) => {
e.stopPropagation();
if (e.keyCode === 13 || e.keyCode === 32) {
ipcRenderer.send('workbenchCommand', 'workbench.extensions.action.disableAll');
ipcRenderer.send('workbenchCommand', 'workbench.action.reloadWindow');
}
});
// Cmd+Enter or Mac or Ctrl+Enter on other platforms previews issue and closes window
if (platform.isMacintosh) {
let prevKeyWasCommand = false;
document.onkeydown = (e: KeyboardEvent) => {
if (prevKeyWasCommand && e.keyCode === 13) {
if (this.createIssue()) {
remote.getCurrentWindow().close();
}
document.onkeydown = (e: KeyboardEvent) => {
const cmdOrCtrlKey = platform.isMacintosh ? e.metaKey : e.ctrlKey;
// Cmd/Ctrl+Enter previews issue and closes window
if (cmdOrCtrlKey && e.keyCode === 13) {
if (this.createIssue()) {
remote.getCurrentWindow().close();
}
}
prevKeyWasCommand = e.keyCode === 91 || e.keyCode === 93;
};
} else {
document.onkeydown = (e: KeyboardEvent) => {
if (e.ctrlKey && e.keyCode === 13) {
if (this.createIssue()) {
remote.getCurrentWindow().close();
}
}
};
}
// Cmd/Ctrl + zooms in
if (cmdOrCtrlKey && e.keyCode === 187) {
this.applyZoom(webFrame.getZoomLevel() + 1);
}
// Cmd/Ctrl - zooms out
if (cmdOrCtrlKey && e.keyCode === 189) {
this.applyZoom(webFrame.getZoomLevel() - 1);
}
};
}
private updatePreviewButtonState() {
@@ -447,7 +446,7 @@ export class IssueReporter extends Disposable {
}
private searchVSCodeIssues(title: string, issueDescription: string): void {
if (title || issueDescription) {
if (title) {
this.searchDuplicates(title, issueDescription);
} else {
this.clearSearchResults();
@@ -578,7 +577,7 @@ export class IssueReporter extends Disposable {
similarIssues.appendChild(issues);
} else {
const message = $('div.list-title');
message.textContent = localize('noResults', "No results found");
message.textContent = localize('noSimilarIssues', "No similar issues found");
similarIssues.appendChild(message);
}
}
@@ -771,10 +770,14 @@ export class IssueReporter extends Disposable {
const target = document.querySelector('.block-system .block-info');
let tableHtml = '';
Object.keys(state.systemInfo).forEach(k => {
const data = typeof state.systemInfo[k] === 'object'
? Object.keys(state.systemInfo[k]).map(key => `${key}: ${state.systemInfo[k][key]}`).join('<br>')
: state.systemInfo[k];
tableHtml += `
<tr>
<td>${k}</td>
<td>${state.systemInfo[k]}</td>
<td>${data}</td>
</tr>`;
});
target.innerHTML = `<table>${tableHtml}</table>`;
@@ -142,7 +142,11 @@ ${this.getInfos()}
`;
Object.keys(this._data.systemInfo).forEach(k => {
md += `|${k}|${this._data.systemInfo[k]}|\n`;
const data = typeof this._data.systemInfo[k] === 'object'
? Object.keys(this._data.systemInfo[k]).map(key => `${key}: ${this._data.systemInfo[k][key]}`).join('<br>')
: this._data.systemInfo[k];
md += `|${k}|${data}|\n`;
});
md += '\n</details>';
@@ -35,6 +35,36 @@ VS Code version: undefined
OS version: undefined
<!-- generated by issue reporter -->`);
});
test('serializes GPU information when data is provided', () => {
const issueReporterModel = new IssueReporterModel({
issueType: 0,
systemInfo: {
'GPU Status': {
'2d_canvas': 'enabled',
'checker_imaging': 'disabled_off'
}
}
});
assert.equal(issueReporterModel.serialize(),
`
Issue Type: <b>Bug</b>
undefined
VS Code version: undefined
OS version: undefined
<details>
<summary>System Info</summary>
|Item|Value|
|---|---|
|GPU Status|2d_canvas: enabled<br>checker_imaging: disabled_off|
</details>Extensions: none
<!-- generated by issue reporter -->`);
});
+12 -2
View File
@@ -29,6 +29,7 @@ export interface SystemInfo {
VM: string;
'Screen Reader': string;
'Process Argv': string;
'GPU Status': Electron.GPUFeatureStatus;
}
export interface ProcessInfo {
@@ -92,7 +93,8 @@ export function getSystemInfo(info: IMainProcessInfo): SystemInfo {
'Memory (System)': `${(os.totalmem() / GB).toFixed(2)}GB (${(os.freemem() / GB).toFixed(2)}GB free)`,
VM: `${Math.round((virtualMachineHint.value() * 100))}%`,
'Screen Reader': `${app.isAccessibilitySupportEnabled() ? 'yes' : 'no'}`,
'Process Argv': `${info.mainArguments.join(' ')}`
'Process Argv': `${info.mainArguments.join(' ')}`,
'GPU Status': app.getGPUFeatureStatus()
};
const cpus = os.cpus();
@@ -208,7 +210,14 @@ function formatLaunchConfigs(configs: WorkspaceStatItem[]): string {
return output.join('\n');
}
function formatEnvironment(info: IMainProcessInfo): string {
function expandGPUFeatures(): string {
const gpuFeatures = app.getGPUFeatureStatus();
const longestFeatureName = Math.max(...Object.keys(gpuFeatures).map(feature => feature.length));
// Make columns aligned by adding spaces after feature name
return Object.keys(gpuFeatures).map(feature => `${feature}: ${repeat(' ', longestFeatureName - feature.length)} ${gpuFeatures[feature]}`).join('\n ');
}
export function formatEnvironment(info: IMainProcessInfo): string {
const MB = 1024 * 1024;
const GB = 1024 * MB;
@@ -226,6 +235,7 @@ function formatEnvironment(info: IMainProcessInfo): string {
output.push(`VM: ${Math.round((virtualMachineHint.value() * 100))}%`);
output.push(`Screen Reader: ${app.isAccessibilitySupportEnabled() ? 'yes' : 'no'}`);
output.push(`Process Argv: ${info.mainArguments.join(' ')}`);
output.push(`GPU Status: ${expandGPUFeatures()}`);
return output.join('\n');
}
@@ -42,6 +42,19 @@ export interface ITextAreaInputHost {
deduceModelPosition(viewAnchorPosition: Position, deltaOffset: number, lineFeedCnt: number): Position;
}
const enum TextAreaInputEventType {
none,
compositionstart,
compositionupdate,
compositionend,
input,
cut,
copy,
paste,
focus,
blur
}
/**
* Writes screen reader content to the textarea and is able to analyze its input events to generate:
* - onCut
@@ -89,6 +102,7 @@ export class TextAreaInput extends Disposable {
private readonly _host: ITextAreaInputHost;
private readonly _textArea: TextAreaWrapper;
private _lastTextAreaEvent: TextAreaInputEventType;
private readonly _asyncTriggerCut: RunOnceScheduler;
private _textAreaState: TextAreaState;
@@ -101,6 +115,7 @@ export class TextAreaInput extends Disposable {
super();
this._host = host;
this._textArea = this._register(new TextAreaWrapper(textArea));
this._lastTextAreaEvent = TextAreaInputEventType.none;
this._asyncTriggerCut = this._register(new RunOnceScheduler(() => this._onCut.fire(), 0));
this._textAreaState = TextAreaState.EMPTY;
@@ -129,6 +144,8 @@ export class TextAreaInput extends Disposable {
}));
this._register(dom.addDisposableListener(textArea.domNode, 'compositionstart', (e: CompositionEvent) => {
this._lastTextAreaEvent = TextAreaInputEventType.compositionstart;
if (this._isDoingComposition) {
return;
}
@@ -145,10 +162,10 @@ export class TextAreaInput extends Disposable {
/**
* Deduce the typed input from a text area's value and the last observed state.
*/
const deduceInputFromTextAreaValue = (couldBeEmojiInput: boolean): [TextAreaState, ITypeData] => {
const deduceInputFromTextAreaValue = (couldBeEmojiInput: boolean, couldBeTypingAtOffset0: boolean): [TextAreaState, ITypeData] => {
const oldState = this._textAreaState;
const newState = this._textAreaState.readFromTextArea(this._textArea);
return [newState, TextAreaState.deduceInput(oldState, newState, couldBeEmojiInput)];
const newState = TextAreaState.readFromTextArea(this._textArea);
return [newState, TextAreaState.deduceInput(oldState, newState, couldBeEmojiInput, couldBeTypingAtOffset0)];
};
/**
@@ -185,6 +202,8 @@ export class TextAreaInput extends Disposable {
};
this._register(dom.addDisposableListener(textArea.domNode, 'compositionupdate', (e: CompositionEvent) => {
this._lastTextAreaEvent = TextAreaInputEventType.compositionupdate;
if (browser.isChromev56) {
// See https://github.com/Microsoft/monaco-editor/issues/320
// where compositionupdate .data is broken in Chrome v55 and v56
@@ -195,7 +214,7 @@ export class TextAreaInput extends Disposable {
}
if (compositionDataInValid(e.locale)) {
const [newState, typeInput] = deduceInputFromTextAreaValue(/*couldBeEmojiInput*/false);
const [newState, typeInput] = deduceInputFromTextAreaValue(/*couldBeEmojiInput*/false, /*couldBeTypingAtOffset0*/false);
this._textAreaState = newState;
this._onType.fire(typeInput);
this._onCompositionUpdate.fire(e);
@@ -209,9 +228,11 @@ export class TextAreaInput extends Disposable {
}));
this._register(dom.addDisposableListener(textArea.domNode, 'compositionend', (e: CompositionEvent) => {
this._lastTextAreaEvent = TextAreaInputEventType.compositionend;
if (compositionDataInValid(e.locale)) {
// https://github.com/Microsoft/monaco-editor/issues/339
const [newState, typeInput] = deduceInputFromTextAreaValue(/*couldBeEmojiInput*/false);
const [newState, typeInput] = deduceInputFromTextAreaValue(/*couldBeEmojiInput*/false, /*couldBeTypingAtOffset0*/false);
this._textAreaState = newState;
this._onType.fire(typeInput);
} else {
@@ -223,7 +244,7 @@ export class TextAreaInput extends Disposable {
// Due to isEdgeOrIE (where the textarea was not cleared initially) and isChrome (the textarea is not updated correctly when composition ends)
// we cannot assume the text at the end consists only of the composited text
if (browser.isEdgeOrIE || browser.isChrome) {
this._textAreaState = this._textAreaState.readFromTextArea(this._textArea);
this._textAreaState = TextAreaState.readFromTextArea(this._textArea);
}
if (!this._isDoingComposition) {
@@ -235,6 +256,10 @@ export class TextAreaInput extends Disposable {
}));
this._register(dom.addDisposableListener(textArea.domNode, 'input', () => {
// We want to find out if this is the first `input` after a `focus`.
const previousEventWasFocus = (this._lastTextAreaEvent === TextAreaInputEventType.focus);
this._lastTextAreaEvent = TextAreaInputEventType.input;
// Pretend here we touched the text area, as the `input` event will most likely
// result in a `selectionchange` event which we want to ignore
this._textArea.setIgnoreSelectionChangeTime('received input event');
@@ -254,7 +279,7 @@ export class TextAreaInput extends Disposable {
return;
}
const [newState, typeInput] = deduceInputFromTextAreaValue(/*couldBeEmojiInput*/platform.isMacintosh);
const [newState, typeInput] = deduceInputFromTextAreaValue(/*couldBeEmojiInput*/platform.isMacintosh, /*couldBeTypingAtOffset0*/previousEventWasFocus && platform.isMacintosh);
if (typeInput.replaceCharCnt === 0 && typeInput.text.length === 1 && strings.isHighSurrogate(typeInput.text.charCodeAt(0))) {
// Ignore invalid input but keep it around for next time
return;
@@ -279,6 +304,8 @@ export class TextAreaInput extends Disposable {
// --- Clipboard operations
this._register(dom.addDisposableListener(textArea.domNode, 'cut', (e: ClipboardEvent) => {
this._lastTextAreaEvent = TextAreaInputEventType.cut;
// Pretend here we touched the text area, as the `cut` event will most likely
// result in a `selectionchange` event which we want to ignore
this._textArea.setIgnoreSelectionChangeTime('received cut event');
@@ -288,10 +315,14 @@ export class TextAreaInput extends Disposable {
}));
this._register(dom.addDisposableListener(textArea.domNode, 'copy', (e: ClipboardEvent) => {
this._lastTextAreaEvent = TextAreaInputEventType.copy;
this._ensureClipboardGetsEditorSelection(e);
}));
this._register(dom.addDisposableListener(textArea.domNode, 'paste', (e: ClipboardEvent) => {
this._lastTextAreaEvent = TextAreaInputEventType.paste;
// Pretend here we touched the text area, as the `paste` event will most likely
// result in a `selectionchange` event which we want to ignore
this._textArea.setIgnoreSelectionChangeTime('received paste event');
@@ -312,8 +343,14 @@ export class TextAreaInput extends Disposable {
}
}));
this._register(dom.addDisposableListener(textArea.domNode, 'focus', () => this._setHasFocus(true)));
this._register(dom.addDisposableListener(textArea.domNode, 'blur', () => this._setHasFocus(false)));
this._register(dom.addDisposableListener(textArea.domNode, 'focus', () => {
this._lastTextAreaEvent = TextAreaInputEventType.focus;
this._setHasFocus(true);
}));
this._register(dom.addDisposableListener(textArea.domNode, 'blur', () => {
this._lastTextAreaEvent = TextAreaInputEventType.blur;
this._setHasFocus(false);
}));
// See https://github.com/Microsoft/vscode/issues/27216
@@ -51,7 +51,7 @@ export class TextAreaState {
return '[ <' + this.value + '>, selectionStart: ' + this.selectionStart + ', selectionEnd: ' + this.selectionEnd + ']';
}
public readFromTextArea(textArea: ITextAreaWrapper): TextAreaState {
public static readFromTextArea(textArea: ITextAreaWrapper): TextAreaState {
return new TextAreaState(textArea.getValue(), textArea.getSelectionStart(), textArea.getSelectionEnd(), null, null);
}
@@ -60,7 +60,7 @@ export class TextAreaState {
}
public writeToTextArea(reason: string, textArea: ITextAreaWrapper, select: boolean): void {
// console.log(Date.now() + ': applyToTextArea ' + reason + ': ' + this.toString());
// console.log(Date.now() + ': writeToTextArea ' + reason + ': ' + this.toString());
textArea.setValue(reason, this.value);
if (select) {
textArea.setSelectionRange(reason, this.selectionStart, this.selectionEnd);
@@ -97,7 +97,7 @@ export class TextAreaState {
return new TextAreaState(text, 0, text.length, null, null);
}
public static deduceInput(previousState: TextAreaState, currentState: TextAreaState, couldBeEmojiInput: boolean): ITypeData {
public static deduceInput(previousState: TextAreaState, currentState: TextAreaState, couldBeEmojiInput: boolean, couldBeTypingAtOffset0: boolean): ITypeData {
if (!previousState) {
// This is the EMPTY state
return {
@@ -117,6 +117,18 @@ export class TextAreaState {
let currentSelectionStart = currentState.selectionStart;
let currentSelectionEnd = currentState.selectionEnd;
if (couldBeTypingAtOffset0 && previousValue.length > 0 && previousSelectionStart === previousSelectionEnd && currentSelectionStart === currentSelectionEnd) {
// See https://github.com/Microsoft/vscode/issues/42251
// where typing always happens at offset 0 in the textarea
// when using a custom title area in OSX and moving the window
if (strings.endsWith(currentValue, previousValue)) {
// Looks like something was typed at offset 0
// ==> pretend we placed the cursor at offset 0 to begin with...
previousSelectionStart = 0;
previousSelectionEnd = 0;
}
}
// Strip the previous suffix from the value (without interfering with the current selection)
const previousSuffix = previousValue.substring(previousSelectionEnd);
const currentSuffix = currentValue.substring(currentSelectionEnd);
@@ -85,8 +85,14 @@ export class DecorationsOverlay extends DynamicViewOverlay {
// Sort decorations for consistent render output
decorations = decorations.sort((a, b) => {
let aClassName = a.options.className;
let bClassName = b.options.className;
if (a.options.zIndex < b.options.zIndex) {
return -1;
}
if (a.options.zIndex > b.options.zIndex) {
return 1;
}
const aClassName = a.options.className;
const bClassName = b.options.className;
if (aClassName < bClassName) {
return -1;
@@ -6,7 +6,6 @@
import * as browser from 'vs/base/browser/browser';
import * as platform from 'vs/base/common/platform';
import * as strings from 'vs/base/common/strings';
import { FastDomNode, createFastDomNode } from 'vs/base/browser/fastDomNode';
import { IConfiguration } from 'vs/editor/common/editorCommon';
import { LineDecoration } from 'vs/editor/common/viewLayout/lineDecorations';
@@ -192,7 +191,8 @@ export class ViewLine implements IVisibleLine {
let renderLineInput = new RenderLineInput(
options.useMonospaceOptimizations,
lineData.content,
lineData.mightContainRTL,
lineData.isBasicASCII,
lineData.containsRTL,
lineData.minColumn - 1,
lineData.tokens,
actualInlineDecorations,
@@ -222,13 +222,8 @@ export class ViewLine implements IVisibleLine {
sb.appendASCIIString('</div>');
let renderedViewLine: IRenderedViewLine = null;
if (canUseFastRenderedViewLine && options.useMonospaceOptimizations && !output.containsForeignElements) {
let isRegularASCII = true;
if (lineData.mightContainNonBasicASCII) {
isRegularASCII = strings.isBasicASCII(lineData.content);
}
if (isRegularASCII && lineData.content.length < 1000 && renderLineInput.lineTokens.getCount() < 100) {
if (canUseFastRenderedViewLine && lineData.isBasicASCII && options.useMonospaceOptimizations && !output.containsForeignElements) {
if (lineData.content.length < 1000 && renderLineInput.lineTokens.getCount() < 100) {
// Browser rounding errors have been observed in Chrome and IE, so using the fast
// view line only for short lines. Please test before removing the length check...
// ---
@@ -27,7 +27,7 @@ import { LineTokens } from 'vs/editor/common/core/lineTokens';
import { Configuration } from 'vs/editor/browser/config/configuration';
import { Position, IPosition } from 'vs/editor/common/core/position';
import { Selection, ISelection } from 'vs/editor/common/core/selection';
import { InlineDecoration, InlineDecorationType } from 'vs/editor/common/viewModel/viewModel';
import { InlineDecoration, InlineDecorationType, ViewLineRenderingData } from 'vs/editor/common/viewModel/viewModel';
import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection';
import { ColorId, MetadataConsts, FontStyle } from 'vs/editor/common/modes';
import { Event, Emitter } from 'vs/base/common/event';
@@ -99,12 +99,7 @@ class VisualEditorState {
this._zonesMap = {};
// (2) Model decorations
if (this._decorations.length > 0) {
editor.changeDecorations((changeAccessor: IModelDecorationsChangeAccessor) => {
changeAccessor.deltaDecorations(this._decorations, []);
});
}
this._decorations = [];
this._decorations = editor.deltaDecorations(this._decorations, []);
}
public apply(editor: CodeEditor, overviewRuler: editorBrowser.IOverviewRuler, newDecorations: IEditorDiffDecorationsWithZones): void {
@@ -1998,10 +1993,13 @@ class InlineViewZonesComputer extends ViewZonesComputer {
sb.appendASCIIString(String(count * config.lineHeight));
sb.appendASCIIString('px;width:1000000px;">');
const isBasicASCII = ViewLineRenderingData.isBasicASCII(lineContent, originalModel.mightContainNonBasicASCII());
const containsRTL = ViewLineRenderingData.containsRTL(lineContent, isBasicASCII, originalModel.mightContainRTL());
renderViewLine(new RenderLineInput(
(config.fontInfo.isMonospace && !config.viewInfo.disableMonospaceOptimizations),
lineContent,
originalModel.mightContainRTL(),
isBasicASCII,
containsRTL,
0,
lineTokens,
actualDecorations,
@@ -2032,27 +2030,31 @@ function createFakeLinesDiv(): HTMLElement {
}
registerThemingParticipant((theme, collector) => {
let added = theme.getColor(diffInserted);
const added = theme.getColor(diffInserted);
if (added) {
collector.addRule(`.monaco-editor .line-insert, .monaco-editor .char-insert { background-color: ${added}; }`);
collector.addRule(`.monaco-diff-editor .line-insert, .monaco-diff-editor .char-insert { background-color: ${added}; }`);
collector.addRule(`.monaco-editor .inline-added-margin-view-zone { background-color: ${added}; }`);
}
let removed = theme.getColor(diffRemoved);
const removed = theme.getColor(diffRemoved);
if (removed) {
collector.addRule(`.monaco-editor .line-delete, .monaco-editor .char-delete { background-color: ${removed}; }`);
collector.addRule(`.monaco-diff-editor .line-delete, .monaco-diff-editor .char-delete { background-color: ${removed}; }`);
collector.addRule(`.monaco-editor .inline-deleted-margin-view-zone { background-color: ${removed}; }`);
}
let addedOutline = theme.getColor(diffInsertedOutline);
const addedOutline = theme.getColor(diffInsertedOutline);
if (addedOutline) {
collector.addRule(`.monaco-editor .line-insert, .monaco-editor .char-insert { border: 1px dashed ${addedOutline}; }`);
collector.addRule(`.monaco-editor .line-insert, .monaco-editor .char-insert { border: 1px ${theme.type === 'hc' ? 'dashed' : 'solid'} ${addedOutline}; }`);
}
let removedOutline = theme.getColor(diffRemovedOutline);
const removedOutline = theme.getColor(diffRemovedOutline);
if (removedOutline) {
collector.addRule(`.monaco-editor .line-delete, .monaco-editor .char-delete { border: 1px dashed ${removedOutline}; }`);
collector.addRule(`.monaco-editor .line-delete, .monaco-editor .char-delete { border: 1px ${theme.type === 'hc' ? 'dashed' : 'solid'} ${removedOutline}; }`);
}
let shadow = theme.getColor(scrollbarShadow);
const shadow = theme.getColor(scrollbarShadow);
if (shadow) {
collector.addRule(`.monaco-diff-editor.side-by-side .editor.modified { box-shadow: -6px 0 5px -5px ${shadow}; }`);
}
+5 -1
View File
@@ -29,6 +29,7 @@ import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService';
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
import { ITextModel, TextModelResolvedOptions } from 'vs/editor/common/model';
import { ViewLineRenderingData } from 'vs/editor/common/viewModel/viewModel';
const DIFF_LINES_PADDING = 3;
@@ -738,10 +739,13 @@ export class DiffReview extends Disposable {
const lineTokens = new LineTokens(tokens, lineContent);
const isBasicASCII = ViewLineRenderingData.isBasicASCII(lineContent, model.mightContainNonBasicASCII());
const containsRTL = ViewLineRenderingData.containsRTL(lineContent, isBasicASCII, model.mightContainRTL());
const r = renderViewLine(new RenderLineInput(
(config.fontInfo.isMonospace && !config.viewInfo.disableMonospaceOptimizations),
lineContent,
model.mightContainRTL(),
isBasicASCII,
containsRTL,
0,
lineTokens,
[],
+6 -2
View File
@@ -81,7 +81,12 @@ export interface IModelDecorationOptions {
* Always render the decoration (even when the range it encompasses is collapsed).
* @internal
*/
readonly showIfCollapsed?: boolean;
showIfCollapsed?: boolean;
/**
* Specifies the stack order of a decoration.
* A decoration with greater stack order is always in front of a decoration with a lower stack order.
*/
zIndex?: number;
/**
* If set, render this decoration in the overview ruler.
*/
@@ -1147,6 +1152,5 @@ export class ApplyEditsResult {
*/
export interface IInternalModelContentChange extends IModelContentChange {
range: Range;
rangeOffset: number;
forceMoveMarkers: boolean;
}
+4 -7
View File
@@ -12,14 +12,11 @@ import { IModelDecoration } from 'vs/editor/common/model';
// The red-black tree is based on the "Introduction to Algorithms" by Cormen, Leiserson and Rivest.
//
/**
* The class name sort order must match the severity order. Highest severity last.
*/
export const ClassName = {
EditorHintDecoration: 'squiggly-a-hint',
EditorInfoDecoration: 'squiggly-b-info',
EditorWarningDecoration: 'squiggly-c-warning',
EditorErrorDecoration: 'squiggly-d-error'
EditorHintDecoration: 'squiggly-hint',
EditorInfoDecoration: 'squiggly-info',
EditorWarningDecoration: 'squiggly-warning',
EditorErrorDecoration: 'squiggly-error'
};
/**
+10 -11
View File
@@ -386,10 +386,11 @@ export class TextModel extends Disposable implements model.ITextModel {
this.setValueFromTextBuffer(textBuffer);
}
private _createContentChanged2(startLineNumber: number, startColumn: number, endLineNumber: number, endColumn: number, rangeLength: number, text: string, isUndoing: boolean, isRedoing: boolean, isFlush: boolean): IModelContentChangedEvent {
private _createContentChanged2(range: Range, rangeOffset: number, rangeLength: number, text: string, isUndoing: boolean, isRedoing: boolean, isFlush: boolean): IModelContentChangedEvent {
return {
changes: [{
range: new Range(startLineNumber, startColumn, endLineNumber, endColumn),
range: range,
rangeOffset: rangeOffset,
rangeLength: rangeLength,
text: text,
}],
@@ -435,7 +436,7 @@ export class TextModel extends Disposable implements model.ITextModel {
false,
false
),
this._createContentChanged2(1, 1, endLineNumber, endColumn, oldModelValueLength, this.getValue(), false, false, true)
this._createContentChanged2(new Range(1, 1, endLineNumber, endColumn), 0, oldModelValueLength, this.getValue(), false, false, true)
);
}
@@ -466,7 +467,7 @@ export class TextModel extends Disposable implements model.ITextModel {
false,
false
),
this._createContentChanged2(1, 1, endLineNumber, endColumn, oldModelValueLength, this.getValue(), false, false, false)
this._createContentChanged2(new Range(1, 1, endLineNumber, endColumn), 0, oldModelValueLength, this.getValue(), false, false, false)
);
}
@@ -2585,22 +2586,20 @@ export class ModelDecorationOverviewRulerOptions implements model.IModelDecorati
}
}
let lastStaticId = 0;
export class ModelDecorationOptions implements model.IModelDecorationOptions {
public static EMPTY: ModelDecorationOptions;
public static register(options: model.IModelDecorationOptions): ModelDecorationOptions {
return new ModelDecorationOptions(++lastStaticId, options);
return new ModelDecorationOptions(options);
}
public static createDynamic(options: model.IModelDecorationOptions): ModelDecorationOptions {
return new ModelDecorationOptions(0, options);
return new ModelDecorationOptions(options);
}
readonly staticId: number;
readonly stickiness: model.TrackedRangeStickiness;
readonly zIndex: number;
readonly className: string;
readonly hoverMessage: IMarkdownString | IMarkdownString[];
readonly glyphMarginHoverMessage: IMarkdownString | IMarkdownString[];
@@ -2614,9 +2613,9 @@ export class ModelDecorationOptions implements model.IModelDecorationOptions {
readonly beforeContentClassName: string;
readonly afterContentClassName: string;
private constructor(staticId: number, options: model.IModelDecorationOptions) {
this.staticId = staticId;
private constructor(options: model.IModelDecorationOptions) {
this.stickiness = options.stickiness || model.TrackedRangeStickiness.AlwaysGrowsWhenTypingAtEdges;
this.zIndex = options.zIndex || 0;
this.className = options.className ? cleanClassName(options.className) : strings.empty;
this.hoverMessage = options.hoverMessage || [];
this.glyphMarginHoverMessage = options.glyphMarginHoverMessage || [];
@@ -32,6 +32,10 @@ export interface IModelContentChange {
* The range that got replaced.
*/
readonly range: IRange;
/**
* The offset of the range that got replaced.
*/
readonly rangeOffset: number;
/**
* The length of the range that got replaced.
*/
@@ -118,26 +118,31 @@ class ModelMarkerHandler {
let className: string;
let color: ThemeColor;
let darkColor: ThemeColor;
let zIndex: number;
switch (marker.severity) {
case MarkerSeverity.Hint:
className = ClassName.EditorHintDecoration;
zIndex = 0;
break;
case MarkerSeverity.Warning:
className = ClassName.EditorWarningDecoration;
color = themeColorFromId(overviewRulerWarning);
darkColor = themeColorFromId(overviewRulerWarning);
zIndex = 20;
break;
case MarkerSeverity.Info:
className = ClassName.EditorInfoDecoration;
color = themeColorFromId(overviewRulerInfo);
darkColor = themeColorFromId(overviewRulerInfo);
zIndex = 10;
break;
case MarkerSeverity.Error:
default:
className = ClassName.EditorErrorDecoration;
color = themeColorFromId(overviewRulerError);
darkColor = themeColorFromId(overviewRulerError);
zIndex = 30;
break;
}
@@ -177,7 +182,8 @@ class ModelMarkerHandler {
color,
darkColor,
position: OverviewRulerLane.Right
}
},
zIndex
};
}
}
@@ -56,27 +56,32 @@ export const overviewRulerInfo = registerColor('editorOverviewRuler.infoForegrou
// contains all color rules that used to defined in editor/browser/widget/editor.css
registerThemingParticipant((theme, collector) => {
let background = theme.getColor(editorBackground);
const background = theme.getColor(editorBackground);
if (background) {
collector.addRule(`.monaco-editor, .monaco-editor-background, .monaco-editor .inputarea.ime-input { background-color: ${background}; }`);
}
let foreground = theme.getColor(editorForeground);
const foreground = theme.getColor(editorForeground);
if (foreground) {
collector.addRule(`.monaco-editor, .monaco-editor .inputarea.ime-input { color: ${foreground}; }`);
}
let gutter = theme.getColor(editorGutter);
const gutter = theme.getColor(editorGutter);
if (gutter) {
collector.addRule(`.monaco-editor .margin { background-color: ${gutter}; }`);
}
let rangeHighlight = theme.getColor(editorRangeHighlight);
const rangeHighlight = theme.getColor(editorRangeHighlight);
if (rangeHighlight) {
collector.addRule(`.monaco-editor .rangeHighlight { background-color: ${rangeHighlight}; }`);
}
let rangeHighlightBorder = theme.getColor(editorRangeHighlightBorder);
const rangeHighlightBorder = theme.getColor(editorRangeHighlightBorder);
if (rangeHighlightBorder) {
collector.addRule(`.monaco-editor .rangeHighlight { border: 1px dotted ${rangeHighlightBorder}; }`);
collector.addRule(`.monaco-editor .rangeHighlight { border: 1px ${theme.type === 'hc' ? 'dotted' : 'solid'} ${rangeHighlightBorder}; }`);
}
let invisibles = theme.getColor(editorWhitespaces);
const invisibles = theme.getColor(editorWhitespaces);
if (invisibles) {
collector.addRule(`.vs-whitespace { color: ${invisibles} !important; }`);
}
@@ -36,7 +36,8 @@ export class RenderLineInput {
public readonly useMonospaceOptimizations: boolean;
public readonly lineContent: string;
public readonly mightContainRTL: boolean;
public readonly isBasicASCII: boolean;
public readonly containsRTL: boolean;
public readonly fauxIndentLength: number;
public readonly lineTokens: IViewLineTokens;
public readonly lineDecorations: LineDecoration[];
@@ -50,7 +51,8 @@ export class RenderLineInput {
constructor(
useMonospaceOptimizations: boolean,
lineContent: string,
mightContainRTL: boolean,
isBasicASCII: boolean,
containsRTL: boolean,
fauxIndentLength: number,
lineTokens: IViewLineTokens,
lineDecorations: LineDecoration[],
@@ -63,7 +65,8 @@ export class RenderLineInput {
) {
this.useMonospaceOptimizations = useMonospaceOptimizations;
this.lineContent = lineContent;
this.mightContainRTL = mightContainRTL;
this.isBasicASCII = isBasicASCII;
this.containsRTL = containsRTL;
this.fauxIndentLength = fauxIndentLength;
this.lineTokens = lineTokens;
this.lineDecorations = lineDecorations;
@@ -85,7 +88,8 @@ export class RenderLineInput {
return (
this.useMonospaceOptimizations === other.useMonospaceOptimizations
&& this.lineContent === other.lineContent
&& this.mightContainRTL === other.mightContainRTL
&& this.isBasicASCII === other.isBasicASCII
&& this.containsRTL === other.containsRTL
&& this.fauxIndentLength === other.fauxIndentLength
&& this.tabSize === other.tabSize
&& this.spaceWidth === other.spaceWidth
@@ -330,11 +334,7 @@ function resolveRenderLineInput(input: RenderLineInput): ResolvedRenderLineInput
}
tokens = _applyInlineDecorations(lineContent, len, tokens, input.lineDecorations);
}
let containsRTL = false;
if (input.mightContainRTL) {
containsRTL = strings.containsRTL(lineContent);
}
if (!containsRTL && !input.fontLigatures) {
if (input.isBasicASCII && !input.fontLigatures) {
tokens = splitLargeTokens(lineContent, tokens);
}
@@ -346,7 +346,7 @@ function resolveRenderLineInput(input: RenderLineInput): ResolvedRenderLineInput
tokens,
containsForeignElements,
input.tabSize,
containsRTL,
input.containsRTL,
input.spaceWidth,
input.renderWhitespace,
input.renderControlCharacters
@@ -406,11 +406,6 @@ function splitLargeTokens(lineContent: string, tokens: LinePart[]): LinePart[] {
const piecesCount = Math.ceil(diff / Constants.LongToken);
for (let j = 1; j < piecesCount; j++) {
let pieceEndIndex = lastTokenEndIndex + (j * Constants.LongToken);
let lastCharInPiece = lineContent.charCodeAt(pieceEndIndex - 1);
if (strings.isHighSurrogate(lastCharInPiece)) {
// Don't cut in the middle of a surrogate pair
pieceEndIndex--;
}
result[resultLen++] = new LinePart(pieceEndIndex, tokenType);
}
result[resultLen++] = new LinePart(tokenEndIndex, tokenType);
+23 -6
View File
@@ -15,6 +15,7 @@ import { Scrollable, IScrollPosition } from 'vs/base/common/scrollable';
import { IPartialViewLinesViewportData } from 'vs/editor/common/viewLayout/viewLinesViewportData';
import { IEditorWhitespace } from 'vs/editor/common/viewLayout/whitespaceComputer';
import { ITheme } from 'vs/platform/theme/common/themeService';
import * as strings from 'vs/base/common/strings';
export interface IViewWhitespaceViewportData {
readonly id: number;
@@ -208,13 +209,13 @@ export class ViewLineRenderingData {
*/
public readonly content: string;
/**
* If set to false, it is guaranteed that `content` contains only LTR chars.
* Describes if `content` contains RTL characters.
*/
public readonly mightContainRTL: boolean;
public readonly containsRTL: boolean;
/**
* If set to false, it is guaranteed that `content` contains only basic ASCII chars.
* Describes if `content` contains non basic ASCII chars.
*/
public readonly mightContainNonBasicASCII: boolean;
public readonly isBasicASCII: boolean;
/**
* The tokens at this view line.
*/
@@ -241,12 +242,28 @@ export class ViewLineRenderingData {
this.minColumn = minColumn;
this.maxColumn = maxColumn;
this.content = content;
this.mightContainRTL = mightContainRTL;
this.mightContainNonBasicASCII = mightContainNonBasicASCII;
this.isBasicASCII = ViewLineRenderingData.isBasicASCII(content, mightContainNonBasicASCII);
this.containsRTL = ViewLineRenderingData.containsRTL(content, this.isBasicASCII, mightContainRTL);
this.tokens = tokens;
this.inlineDecorations = inlineDecorations;
this.tabSize = tabSize;
}
public static isBasicASCII(lineContent: string, mightContainNonBasicASCII: boolean): boolean {
if (mightContainNonBasicASCII) {
return strings.isBasicASCII(lineContent);
}
return true;
}
public static containsRTL(lineContent: string, isBasicASCII: boolean, mightContainRTL: boolean): boolean {
if (!isBasicASCII && mightContainRTL) {
return strings.containsRTL(lineContent);
}
return false;
}
}
export const enum InlineDecorationType {
@@ -16,6 +16,7 @@ import { ColorProviderRegistry } from 'vs/editor/common/modes';
import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService';
import { getColors, IColorData } from 'vs/editor/contrib/colorPicker/color';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { ModelDecorationOptions } from 'vs/editor/common/model/textModel';
const MAX_DECORATORS = 500;
@@ -153,7 +154,7 @@ export class ColorDetector implements IEditorContribution {
endLineNumber: c.colorInfo.range.endLineNumber,
endColumn: c.colorInfo.range.endColumn
},
options: {}
options: ModelDecorationOptions.EMPTY
}));
this._decorationsIds = this._editor.deltaDecorations(this._decorationsIds, decorations);
@@ -336,10 +336,10 @@ export class LineCommentCommand implements editorCommon.ICommand {
}
return new Selection(
result.startLineNumber,
result.startColumn + this._deltaColumn,
result.endLineNumber,
result.endColumn + this._deltaColumn
result.selectionStartLineNumber,
result.selectionStartColumn + this._deltaColumn,
result.positionLineNumber,
result.positionColumn + this._deltaColumn
);
}
@@ -256,7 +256,7 @@ suite('Editor Contrib - Line Comment Command', () => {
'\t!@# some text',
'\t!@# some more text'
],
new Selection(1, 1, 2, 2)
new Selection(2, 2, 1, 1)
);
});
@@ -271,7 +271,7 @@ suite('Editor Contrib - Line Comment Command', () => {
'\t!@# some text',
' !@# some more text'
],
new Selection(1, 1, 2, 2)
new Selection(2, 2, 1, 1)
);
});
@@ -290,7 +290,7 @@ suite('Editor Contrib - Line Comment Command', () => {
'',
'\t!@# some more text'
],
new Selection(1, 1, 4, 2)
new Selection(4, 2, 1, 1)
);
});
@@ -307,7 +307,7 @@ suite('Editor Contrib - Line Comment Command', () => {
'\t ',
'\t\tsome more text'
],
new Selection(1, 1, 3, 2)
new Selection(3, 2, 1, 1)
);
});
@@ -324,7 +324,7 @@ suite('Editor Contrib - Line Comment Command', () => {
'\t!@# ',
'\t\tsome more text'
],
new Selection(1, 1, 3, 1)
new Selection(3, 1, 1, 1)
);
});
@@ -369,7 +369,7 @@ suite('Editor Contrib - Line Comment Command', () => {
'first!@#',
'\t!@# second line'
],
new Selection(2, 1, 2, 7)
new Selection(2, 7, 2, 1)
);
});
@@ -390,7 +390,7 @@ suite('Editor Contrib - Line Comment Command', () => {
'fourth line',
'fifth'
],
new Selection(1, 5, 2, 1)
new Selection(2, 1, 1, 5)
);
});
@@ -411,7 +411,7 @@ suite('Editor Contrib - Line Comment Command', () => {
'fourth line',
'fifth'
],
new Selection(1, 5, 2, 8)
new Selection(2, 8, 1, 5)
);
});
@@ -432,7 +432,7 @@ suite('Editor Contrib - Line Comment Command', () => {
'!@# fourth line',
'fifth'
],
new Selection(3, 5, 4, 8)
new Selection(4, 8, 3, 5)
);
});
@@ -493,7 +493,7 @@ suite('Editor Contrib - Line Comment Command', () => {
'fourth line',
'fifth'
],
new Selection(1, 5, 2, 8)
new Selection(2, 8, 1, 5)
);
testLineCommentCommand(
@@ -512,7 +512,7 @@ suite('Editor Contrib - Line Comment Command', () => {
'fourth line',
'fifth'
],
new Selection(1, 1, 2, 3)
new Selection(2, 3, 1, 1)
);
});
@@ -607,6 +607,21 @@ suite('Editor Contrib - Line Comment Command', () => {
new Selection(1, 1, 8, 60)
);
});
test('issue #47004: Toggle comments shouldn\'t move cursor', () => {
testAddLineCommentCommand(
[
' A line',
' Another line'
],
new Selection(2, 7, 1, 1),
[
' !@# A line',
' !@# Another line'
],
new Selection(2, 11, 1, 1)
);
});
});
suite('Editor Contrib - Line Comment As Block Comment', () => {
@@ -655,7 +670,7 @@ suite('Editor Contrib - Line Comment As Block Comment', () => {
'fourth line',
'fifth'
],
new Selection(1, 1, 1, 6)
new Selection(1, 6, 1, 1)
);
});
@@ -697,7 +712,7 @@ suite('Editor Contrib - Line Comment As Block Comment', () => {
'fourth line',
'fifth'
],
new Selection(1, 5, 3, 2)
new Selection(3, 2, 1, 5)
);
testLineCommentCommand(
@@ -716,7 +731,7 @@ suite('Editor Contrib - Line Comment As Block Comment', () => {
'fourth line',
'fifth'
],
new Selection(1, 1, 3, 11)
new Selection(3, 11, 1, 1)
);
});
});
@@ -842,7 +857,7 @@ suite('Editor Contrib - Line Comment As Block Comment 2', () => {
'fourth line',
'\t\tfifth\t\t'
],
new Selection(5, 3, 5, 8)
new Selection(5, 8, 5, 3)
);
testLineCommentCommand(
@@ -861,7 +876,7 @@ suite('Editor Contrib - Line Comment As Block Comment 2', () => {
'fourth line',
'\t\tfifth\t\t'
],
new Selection(5, 3, 5, 8)
new Selection(5, 8, 5, 3)
);
testLineCommentCommand(
+6 -11
View File
@@ -173,22 +173,17 @@ export class DragAndDropController implements editorCommon.IEditorContribution {
});
public showAt(position: Position): void {
this._editor.changeDecorations(changeAccessor => {
let newDecorations: IModelDeltaDecoration[] = [];
newDecorations.push({
range: new Range(position.lineNumber, position.column, position.lineNumber, position.column),
options: DragAndDropController._DECORATION_OPTIONS
});
let newDecorations: IModelDeltaDecoration[] = [{
range: new Range(position.lineNumber, position.column, position.lineNumber, position.column),
options: DragAndDropController._DECORATION_OPTIONS
}];
this._dndDecorationIds = changeAccessor.deltaDecorations(this._dndDecorationIds, newDecorations);
});
this._dndDecorationIds = this._editor.deltaDecorations(this._dndDecorationIds, newDecorations);
this._editor.revealPosition(position, editorCommon.ScrollType.Immediate);
}
private _removeDecoration(): void {
this._editor.changeDecorations(changeAccessor => {
changeAccessor.deltaDecorations(this._dndDecorationIds, []);
});
this._dndDecorationIds = this._editor.deltaDecorations(this._dndDecorationIds, []);
}
private _hitContent(target: IMouseTarget): boolean {
@@ -267,6 +267,7 @@ export class FindDecorations implements IDisposable {
private static readonly _CURRENT_FIND_MATCH_DECORATION = ModelDecorationOptions.register({
stickiness: TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges,
zIndex: 13,
className: 'currentFindMatch',
showIfCollapsed: true,
overviewRuler: {
+19 -17
View File
@@ -1066,48 +1066,50 @@ export class SimpleButton extends Widget {
// theming
registerThemingParticipant((theme, collector) => {
function addBackgroundColorRule(selector: string, color: Color): void {
const addBackgroundColorRule = (selector: string, color: Color): void => {
if (color) {
collector.addRule(`.monaco-editor ${selector} { background-color: ${color}; }`);
}
}
};
addBackgroundColorRule('.findMatch', theme.getColor(editorFindMatchHighlight));
addBackgroundColorRule('.currentFindMatch', theme.getColor(editorFindMatch));
addBackgroundColorRule('.findScope', theme.getColor(editorFindRangeHighlight));
let widgetBackground = theme.getColor(editorWidgetBackground);
const widgetBackground = theme.getColor(editorWidgetBackground);
addBackgroundColorRule('.find-widget', widgetBackground);
let widgetShadowColor = theme.getColor(widgetShadow);
const widgetShadowColor = theme.getColor(widgetShadow);
if (widgetShadowColor) {
collector.addRule(`.monaco-editor .find-widget { box-shadow: 0 2px 8px ${widgetShadowColor}; }`);
}
let findMatchHighlightBorder = theme.getColor(editorFindMatchHighlightBorder);
const findMatchHighlightBorder = theme.getColor(editorFindMatchHighlightBorder);
if (findMatchHighlightBorder) {
collector.addRule(`.monaco-editor .findMatch { border: 1px dotted ${findMatchHighlightBorder}; -moz-box-sizing: border-box; box-sizing: border-box; }`);
}
let findMatchBorder = theme.getColor(editorFindMatchBorder);
if (findMatchBorder) {
collector.addRule(`.monaco-editor .currentFindMatch { border: 2px solid ${findMatchBorder}; padding: 1px; -moz-box-sizing: border-box; box-sizing: border-box; }`);
}
let findRangeHighlightBorder = theme.getColor(editorFindRangeHighlightBorder);
if (findRangeHighlightBorder) {
collector.addRule(`.monaco-editor .findScope { border: 1px dashed ${findRangeHighlightBorder}; }`);
collector.addRule(`.monaco-editor .findMatch { border: 1px ${theme.type === 'hc' ? 'dotted' : 'solid'} ${findMatchHighlightBorder}; box-sizing: border-box; }`);
}
let hcBorder = theme.getColor(contrastBorder);
const findMatchBorder = theme.getColor(editorFindMatchBorder);
if (findMatchBorder) {
collector.addRule(`.monaco-editor .currentFindMatch { border: 2px solid ${findMatchBorder}; padding: 1px; box-sizing: border-box; }`);
}
const findRangeHighlightBorder = theme.getColor(editorFindRangeHighlightBorder);
if (findRangeHighlightBorder) {
collector.addRule(`.monaco-editor .findScope { border: 1px ${theme.type === 'hc' ? 'dashed' : 'solid'} ${findRangeHighlightBorder}; }`);
}
const hcBorder = theme.getColor(contrastBorder);
if (hcBorder) {
collector.addRule(`.monaco-editor .find-widget { border: 2px solid ${hcBorder}; }`);
}
let error = theme.getColor(errorForeground);
const error = theme.getColor(errorForeground);
if (error) {
collector.addRule(`.monaco-editor .find-widget.no-results .matchesCount { color: ${error}; }`);
}
let border = theme.getColor(editorWidgetBorder);
const border = theme.getColor(editorWidgetBorder);
if (border) {
collector.addRule(`.monaco-editor .find-widget .monaco-sash { background-color: ${border}; width: 3px !important; margin-left: -4px;}`);
}
@@ -10,18 +10,18 @@ import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
export class FoldingDecorationProvider implements IDecorationProvider {
private COLLAPSED_VISUAL_DECORATION = ModelDecorationOptions.register({
private static COLLAPSED_VISUAL_DECORATION = ModelDecorationOptions.register({
stickiness: TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges,
afterContentClassName: 'inline-folded',
linesDecorationsClassName: 'folding collapsed'
});
private EXPANDED_AUTO_HIDE_VISUAL_DECORATION = ModelDecorationOptions.register({
private static EXPANDED_AUTO_HIDE_VISUAL_DECORATION = ModelDecorationOptions.register({
stickiness: TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges,
linesDecorationsClassName: 'folding'
});
private EXPANDED_VISUAL_DECORATION = ModelDecorationOptions.register({
private static EXPANDED_VISUAL_DECORATION = ModelDecorationOptions.register({
stickiness: TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges,
linesDecorationsClassName: 'folding alwaysShowFoldIcons'
});
@@ -33,11 +33,11 @@ export class FoldingDecorationProvider implements IDecorationProvider {
getDecorationOption(isCollapsed: boolean): ModelDecorationOptions {
if (isCollapsed) {
return this.COLLAPSED_VISUAL_DECORATION;
return FoldingDecorationProvider.COLLAPSED_VISUAL_DECORATION;
} else if (this.autoHideFoldingControls) {
return this.EXPANDED_AUTO_HIDE_VISUAL_DECORATION;
return FoldingDecorationProvider.EXPANDED_AUTO_HIDE_VISUAL_DECORATION;
} else {
return this.EXPANDED_VISUAL_DECORATION;
return FoldingDecorationProvider.EXPANDED_VISUAL_DECORATION;
}
}
@@ -10,7 +10,6 @@ import { TPromise } from 'vs/base/common/winjs.base';
import { Range } from 'vs/editor/common/core/range';
import { Selection } from 'vs/editor/common/core/selection';
import { IEditorContribution } from 'vs/editor/common/editorCommon';
import { IModelDecorationsChangeAccessor } from 'vs/editor/common/model';
import { EditorContextKeys } from 'vs/editor/common/editorContextKeys';
import { registerEditorAction, ServicesAccessor, EditorAction, registerEditorContribution } from 'vs/editor/browser/editorExtensions';
import { IInplaceReplaceSupportResult } from 'vs/editor/common/modes';
@@ -131,9 +130,7 @@ class InPlaceReplaceController implements IEditorContribution {
this.decorationRemover.cancel();
this.decorationRemover = TPromise.timeout(350);
this.decorationRemover.then(() => {
this.editor.changeDecorations((accessor: IModelDecorationsChangeAccessor) => {
this.decorationIds = accessor.deltaDecorations(this.decorationIds, []);
});
this.decorationIds = this.editor.deltaDecorations(this.decorationIds, []);
});
});
}
+20 -22
View File
@@ -251,32 +251,30 @@ class LinkDetector implements editorCommon.IEditorContribution {
private updateDecorations(links: Link[]): void {
const useMetaKey = (this.editor.getConfiguration().multiCursorModifier === 'altKey');
this.editor.changeDecorations((changeAccessor: IModelDecorationsChangeAccessor) => {
var oldDecorations: string[] = [];
let keys = Object.keys(this.currentOccurrences);
for (let i = 0, len = keys.length; i < len; i++) {
let decorationId = keys[i];
let occurance = this.currentOccurrences[decorationId];
oldDecorations.push(occurance.decorationId);
}
let oldDecorations: string[] = [];
let keys = Object.keys(this.currentOccurrences);
for (let i = 0, len = keys.length; i < len; i++) {
let decorationId = keys[i];
let occurance = this.currentOccurrences[decorationId];
oldDecorations.push(occurance.decorationId);
}
var newDecorations: IModelDeltaDecoration[] = [];
if (links) {
// Not sure why this is sometimes null
for (var i = 0; i < links.length; i++) {
newDecorations.push(LinkOccurrence.decoration(links[i], useMetaKey));
}
let newDecorations: IModelDeltaDecoration[] = [];
if (links) {
// Not sure why this is sometimes null
for (let i = 0; i < links.length; i++) {
newDecorations.push(LinkOccurrence.decoration(links[i], useMetaKey));
}
}
var decorations = changeAccessor.deltaDecorations(oldDecorations, newDecorations);
let decorations = this.editor.deltaDecorations(oldDecorations, newDecorations);
this.currentOccurrences = {};
this.activeLinkDecorationId = null;
for (let i = 0, len = decorations.length; i < len; i++) {
var occurance = new LinkOccurrence(links[i], decorations[i]);
this.currentOccurrences[occurance.decorationId] = occurance;
}
});
this.currentOccurrences = {};
this.activeLinkDecorationId = null;
for (let i = 0, len = decorations.length; i < len; i++) {
let occurance = new LinkOccurrence(links[i], decorations[i]);
this.currentOccurrences[occurance.decorationId] = occurance;
}
}
private _onEditorMouseMove(mouseEvent: ClickLinkMouseEvent, withKey?: ClickLinkKeyboardEvent): void {
@@ -803,9 +803,7 @@ export class SelectionHighlighter extends Disposable implements IEditorContribut
this.state = state;
if (!this.state) {
if (this.decorations.length > 0) {
this.decorations = this.editor.deltaDecorations(this.decorations, []);
}
this.decorations = this.editor.deltaDecorations(this.decorations, []);
return;
}
@@ -84,28 +84,25 @@ class DecorationsManager implements IDisposable {
private _addDecorations(reference: FileReferences): void {
this._callOnModelChange.push(this._editor.getModel().onDidChangeDecorations((event) => this._onDecorationChanged()));
this._editor.changeDecorations(accessor => {
const newDecorations: IModelDeltaDecoration[] = [];
const newDecorationsActualIndex: number[] = [];
const newDecorations: IModelDeltaDecoration[] = [];
const newDecorationsActualIndex: number[] = [];
for (let i = 0, len = reference.children.length; i < len; i++) {
let oneReference = reference.children[i];
if (this._decorationIgnoreSet.has(oneReference.id)) {
continue;
}
newDecorations.push({
range: oneReference.range,
options: DecorationsManager.DecorationOptions
});
newDecorationsActualIndex.push(i);
for (let i = 0, len = reference.children.length; i < len; i++) {
let oneReference = reference.children[i];
if (this._decorationIgnoreSet.has(oneReference.id)) {
continue;
}
newDecorations.push({
range: oneReference.range,
options: DecorationsManager.DecorationOptions
});
newDecorationsActualIndex.push(i);
}
const decorations = accessor.deltaDecorations([], newDecorations);
for (let i = 0; i < decorations.length; i++) {
this._decorations.set(decorations[i], reference.children[newDecorationsActualIndex[i]]);
}
});
const decorations = this._editor.deltaDecorations([], newDecorations);
for (let i = 0; i < decorations.length; i++) {
this._decorations.set(decorations[i], reference.children[newDecorationsActualIndex[i]]);
}
}
private _onDecorationChanged(): void {
@@ -143,21 +140,19 @@ class DecorationsManager implements IDisposable {
}
});
this._editor.changeDecorations((accessor) => {
for (let i = 0, len = toRemove.length; i < len; i++) {
this._decorations.delete(toRemove[i]);
}
accessor.deltaDecorations(toRemove, []);
});
for (let i = 0, len = toRemove.length; i < len; i++) {
this._decorations.delete(toRemove[i]);
}
this._editor.deltaDecorations(toRemove, []);
}
public removeDecorations(): void {
this._editor.changeDecorations(accessor => {
this._decorations.forEach((value, key) => {
accessor.removeDecoration(key);
});
this._decorations.clear();
let toRemove: string[] = [];
this._decorations.forEach((value, key) => {
toRemove.push(key);
});
this._editor.deltaDecorations(toRemove, []);
this._decorations.clear();
}
}
@@ -50,7 +50,9 @@ export class OneSnippet {
dispose(): void {
if (this._placeholderDecorations) {
this._editor.changeDecorations(accessor => this._placeholderDecorations.forEach(handle => accessor.removeDecoration(handle)));
let toRemove: string[] = [];
this._placeholderDecorations.forEach(handle => toRemove.push(handle));
this._editor.deltaDecorations(toRemove, []);
}
this._placeholderGroups.length = 0;
}
+15 -9
View File
@@ -66,18 +66,24 @@ export class LRUMemory extends Memory {
// in order of completions, select the first
// that has been used in the past
let { word } = model.getWordUntilPosition(pos);
if (word.length !== 0) {
return 0;
}
let lineSuffix = model.getLineContent(pos.lineNumber).substr(pos.column - 10, pos.column - 1);
if (/\s$/.test(lineSuffix)) {
return 0;
}
let res = 0;
let seq = -1;
if (word.length === 0) {
for (let i = 0; i < items.length; i++) {
const { suggestion } = items[i];
const key = `${model.getLanguageIdentifier().language}/${suggestion.label}`;
const item = this._cache.get(key);
if (item && item.touch > seq && item.type === suggestion.type && item.insertText === suggestion.insertText) {
seq = item.touch;
res = i;
}
for (let i = 0; i < items.length; i++) {
const { suggestion } = items[i];
const key = `${model.getLanguageIdentifier().language}/${suggestion.label}`;
const item = this._cache.get(key);
if (item && item.touch > seq && item.type === suggestion.type && item.insertText === suggestion.insertText) {
seq = item.touch;
res = i;
}
}
return res;
@@ -21,7 +21,7 @@ suite('SuggestMemories', function () {
setup(function () {
pos = { lineNumber: 1, column: 1 };
buffer = TextModel.createFromString('This is some text');
buffer = TextModel.createFromString('This is some text.\nthis.\nfoo: ,');
items = [
createSuggestItem('foo', 0),
createSuggestItem('bar', 0)
@@ -39,7 +39,9 @@ suite('SuggestMemories', function () {
mem.memorize(buffer, pos, null);
});
test('ShyMemories', function () {
test('LRUMemory', function () {
pos = { lineNumber: 2, column: 6 };
const mem = new LRUMemory();
mem.memorize(buffer, pos, items[1]);
@@ -59,7 +61,19 @@ suite('SuggestMemories', function () {
createSuggestItem('new1', 0),
createSuggestItem('new2', 0)
]), 0);
});
test('intellisense is not showing top options first #43429', function () {
// ensure we don't memorize for whitespace prefixes
pos = { lineNumber: 2, column: 6 };
const mem = new LRUMemory();
mem.memorize(buffer, pos, items[1]);
assert.equal(mem.select(buffer, pos, items), 1);
assert.equal(mem.select(buffer, { lineNumber: 3, column: 5 }, items), 0); // foo: |,
assert.equal(mem.select(buffer, { lineNumber: 3, column: 6 }, items), 1); // foo: ,|
});
test('PrefixMemory', function () {
@@ -504,42 +504,34 @@ registerEditorAction(NextWordHighlightAction);
registerEditorAction(PrevWordHighlightAction);
registerThemingParticipant((theme, collector) => {
let selectionHighlight = theme.getColor(editorSelectionHighlight);
const selectionHighlight = theme.getColor(editorSelectionHighlight);
if (selectionHighlight) {
collector.addRule(`.monaco-editor .focused .selectionHighlight { background-color: ${selectionHighlight}; }`);
collector.addRule(`.monaco-editor .selectionHighlight { background-color: ${selectionHighlight.transparent(0.5)}; }`);
}
let wordHighlight = theme.getColor(editorWordHighlight);
const wordHighlight = theme.getColor(editorWordHighlight);
if (wordHighlight) {
collector.addRule(`.monaco-editor .wordHighlight { background-color: ${wordHighlight}; }`);
}
let wordHighlightStrong = theme.getColor(editorWordHighlightStrong);
const wordHighlightStrong = theme.getColor(editorWordHighlightStrong);
if (wordHighlightStrong) {
collector.addRule(`.monaco-editor .wordHighlightStrong { background-color: ${wordHighlightStrong}; }`);
}
let selectionHighlightBorder = theme.getColor(editorSelectionHighlightBorder);
const selectionHighlightBorder = theme.getColor(editorSelectionHighlightBorder);
if (selectionHighlightBorder) {
if (theme.type === 'hc') {
collector.addRule(`.monaco-editor .selectionHighlight { border: 1px dotted ${selectionHighlightBorder}; box-sizing: border-box; }`);
} else {
collector.addRule(`.monaco-editor .selectionHighlight { border: 1px solid ${selectionHighlightBorder}; box-sizing: border-box; }`);
}
}
let wordHighlightBorder = theme.getColor(editorWordHighlightBorder);
if (wordHighlightBorder) {
if (theme.type === 'hc') {
collector.addRule(`.monaco-editor .wordHighlight { border: 1px dashed ${wordHighlightBorder}; box-sizing: border-box; }`);
} else {
collector.addRule(`.monaco-editor .wordHighlight { border: 1px solid ${wordHighlightBorder}; box-sizing: border-box; }`);
}
}
let wordHighlightStrongBorder = theme.getColor(editorWordHighlightStrongBorder);
if (wordHighlightStrongBorder) {
if (theme.type === 'hc') {
collector.addRule(`.monaco-editor .wordHighlightStrong { border: 1px dashed ${wordHighlightStrongBorder}; box-sizing: border-box; }`);
} else {
collector.addRule(`.monaco-editor .wordHighlightStrong { border: 1px solid ${wordHighlightStrongBorder}; box-sizing: border-box; }`);
}
collector.addRule(`.monaco-editor .selectionHighlight { border: 1px ${theme.type === 'hc' ? 'dotted' : 'solid'} ${selectionHighlightBorder}; box-sizing: border-box; }`);
}
const wordHighlightBorder = theme.getColor(editorWordHighlightBorder);
if (wordHighlightBorder) {
collector.addRule(`.monaco-editor .wordHighlight { border: 1px ${theme.type === 'hc' ? 'dashed' : 'solid'} ${wordHighlightBorder}; box-sizing: border-box; }`);
}
const wordHighlightStrongBorder = theme.getColor(editorWordHighlightStrongBorder);
if (wordHighlightStrongBorder) {
collector.addRule(`.monaco-editor .wordHighlightStrong { border: 1px ${theme.type === 'hc' ? 'dashed' : 'solid'} ${wordHighlightStrongBorder}; box-sizing: border-box; }`);
}
});
+15 -5
View File
@@ -13,6 +13,7 @@ import { renderViewLine2 as renderViewLine, RenderLineInput } from 'vs/editor/co
import { LineTokens, IViewLineTokens } from 'vs/editor/common/core/lineTokens';
import * as strings from 'vs/base/common/strings';
import { IStandaloneThemeService } from 'vs/editor/standalone/common/standaloneThemeService';
import { ViewLineRenderingData } from 'vs/editor/common/viewModel/viewModel';
export interface IColorizerOptions {
tabSize?: number;
@@ -93,11 +94,14 @@ export class Colorizer {
});
}
public static colorizeLine(line: string, mightContainRTL: boolean, tokens: IViewLineTokens, tabSize: number = 4): string {
public static colorizeLine(line: string, mightContainNonBasicASCII: boolean, mightContainRTL: boolean, tokens: IViewLineTokens, tabSize: number = 4): string {
const isBasicASCII = ViewLineRenderingData.isBasicASCII(line, mightContainNonBasicASCII);
const containsRTL = ViewLineRenderingData.containsRTL(line, isBasicASCII, mightContainRTL);
let renderResult = renderViewLine(new RenderLineInput(
false,
line,
mightContainRTL,
isBasicASCII,
containsRTL,
0,
tokens,
[],
@@ -116,7 +120,7 @@ export class Colorizer {
model.forceTokenization(lineNumber);
let tokens = model.getLineTokens(lineNumber);
let inflatedTokens = tokens.inflate();
return this.colorizeLine(content, model.mightContainRTL(), inflatedTokens, tabSize);
return this.colorizeLine(content, model.mightContainNonBasicASCII(), model.mightContainRTL(), inflatedTokens, tabSize);
}
}
@@ -143,10 +147,13 @@ function _fakeColorize(lines: string[], tabSize: number): string {
tokens[0] = line.length;
const lineTokens = new LineTokens(tokens, line);
const isBasicASCII = ViewLineRenderingData.isBasicASCII(line, /* check for basic ASCII */true);
const containsRTL = ViewLineRenderingData.containsRTL(line, isBasicASCII, /* check for RTL */true);
let renderResult = renderViewLine(new RenderLineInput(
false,
line,
false,
isBasicASCII,
containsRTL,
0,
lineTokens,
[],
@@ -174,10 +181,13 @@ function _actualColorize(lines: string[], tabSize: number, tokenizationSupport:
let tokenizeResult = tokenizationSupport.tokenize2(line, state, 0);
LineTokens.convertToEndOffset(tokenizeResult.tokens, line.length);
let lineTokens = new LineTokens(tokenizeResult.tokens, line);
const isBasicASCII = ViewLineRenderingData.isBasicASCII(line, /* check for basic ASCII */true);
const containsRTL = ViewLineRenderingData.containsRTL(line, isBasicASCII, /* check for RTL */true);
let renderResult = renderViewLine(new RenderLineInput(
false,
line,
true/* check for RTL */,
isBasicASCII,
containsRTL,
0,
lineTokens.inflate(),
[],
@@ -14,7 +14,7 @@ import { registerEditorContribution, IActionOptions, EditorAction } from 'vs/edi
import { IThemeService } from 'vs/platform/theme/common/themeService';
import { Range } from 'vs/editor/common/core/range';
import { ModelDecorationOptions } from 'vs/editor/common/model/textModel';
import { IModelDecorationsChangeAccessor, IModelDeltaDecoration } from 'vs/editor/common/model';
import { IModelDeltaDecoration } from 'vs/editor/common/model';
export interface IQuickOpenControllerOpts {
inputAriaLabel: string;
@@ -100,31 +100,27 @@ export class QuickOpenController implements editorCommon.IEditorContribution, ID
});
public decorateLine(range: Range, editor: ICodeEditor): void {
editor.changeDecorations((changeAccessor: IModelDecorationsChangeAccessor) => {
const oldDecorations: string[] = [];
if (this.rangeHighlightDecorationId) {
oldDecorations.push(this.rangeHighlightDecorationId);
this.rangeHighlightDecorationId = null;
const oldDecorations: string[] = [];
if (this.rangeHighlightDecorationId) {
oldDecorations.push(this.rangeHighlightDecorationId);
this.rangeHighlightDecorationId = null;
}
const newDecorations: IModelDeltaDecoration[] = [
{
range: range,
options: QuickOpenController._RANGE_HIGHLIGHT_DECORATION
}
];
const newDecorations: IModelDeltaDecoration[] = [
{
range: range,
options: QuickOpenController._RANGE_HIGHLIGHT_DECORATION
}
];
const decorations = changeAccessor.deltaDecorations(oldDecorations, newDecorations);
this.rangeHighlightDecorationId = decorations[0];
});
const decorations = editor.deltaDecorations(oldDecorations, newDecorations);
this.rangeHighlightDecorationId = decorations[0];
}
public clearDecorations(): void {
if (this.rangeHighlightDecorationId) {
this.editor.changeDecorations((changeAccessor: IModelDecorationsChangeAccessor) => {
changeAccessor.deltaDecorations([this.rangeHighlightDecorationId], []);
this.rangeHighlightDecorationId = null;
});
this.editor.deltaDecorations([this.rangeHighlightDecorationId], []);
this.rangeHighlightDecorationId = null;
}
}
}
@@ -38,7 +38,7 @@ import { ResolvedKeybindingItem } from 'vs/platform/keybinding/common/resolvedKe
import { OS } from 'vs/base/common/platform';
import { IRange } from 'vs/editor/common/core/range';
import { ITextModel } from 'vs/editor/common/model';
import { INotificationService, INotification, INotificationHandle, NoOpNotification, PromptOption } from 'vs/platform/notification/common/notification';
import { INotificationService, INotification, INotificationHandle, NoOpNotification, IPromptChoice } from 'vs/platform/notification/common/notification';
import { IConfirmation, IConfirmationResult, IDialogService, IDialogOptions } from 'vs/platform/dialogs/common/dialogs';
import { IPosition, Position as Pos } from 'vs/editor/common/core/position';
@@ -297,8 +297,8 @@ export class SimpleNotificationService implements INotificationService {
return SimpleNotificationService.NO_OP;
}
public prompt(severity: Severity, message: string, choices: PromptOption[]): TPromise<number> {
return TPromise.as(0);
public prompt(severity: Severity, message: string, choices: IPromptChoice[], onCancel?: () => void): INotificationHandle {
return SimpleNotificationService.NO_OP;
}
}
@@ -82,7 +82,7 @@ suite('TextAreaState', () => {
textArea._value = 'Hello world!';
textArea._selectionStart = 1;
textArea._selectionEnd = 12;
let actual = TextAreaState.EMPTY.readFromTextArea(textArea);
let actual = TextAreaState.readFromTextArea(textArea);
assertTextAreaState(actual, 'Hello world!', 1, 12);
assert.equal(actual.value, 'Hello world!');
@@ -124,7 +124,7 @@ suite('TextAreaState', () => {
textArea.dispose();
});
function testDeduceInput(prevState: TextAreaState, value: string, selectionStart: number, selectionEnd: number, expected: string, expectedCharReplaceCnt: number): void {
function testDeduceInput(prevState: TextAreaState, value: string, selectionStart: number, selectionEnd: number, couldBeEmojiInput: boolean, couldBeTypingAtOffset0: boolean, expected: string, expectedCharReplaceCnt: number): void {
prevState = prevState || TextAreaState.EMPTY;
let textArea = new MockTextAreaWrapper();
@@ -132,8 +132,8 @@ suite('TextAreaState', () => {
textArea._selectionStart = selectionStart;
textArea._selectionEnd = selectionEnd;
let newState = prevState.readFromTextArea(textArea);
let actual = TextAreaState.deduceInput(prevState, newState, true);
let newState = TextAreaState.readFromTextArea(textArea);
let actual = TextAreaState.deduceInput(prevState, newState, couldBeEmojiInput, couldBeTypingAtOffset0);
assert.equal(actual.text, expected);
assert.equal(actual.replaceCharCnt, expectedCharReplaceCnt);
@@ -154,7 +154,7 @@ suite('TextAreaState', () => {
testDeduceInput(
TextAreaState.EMPTY,
'',
0, 1,
0, 1, true, false,
'', 0
);
@@ -164,7 +164,7 @@ suite('TextAreaState', () => {
testDeduceInput(
new TextAreaState('', 0, 1, null, null),
'せ',
0, 1,
0, 1, true, false,
'せ', 1
);
@@ -174,7 +174,7 @@ suite('TextAreaState', () => {
testDeduceInput(
new TextAreaState('せ', 0, 1, null, null),
'せn',
0, 2,
0, 2, true, false,
'せn', 1
);
@@ -184,7 +184,7 @@ suite('TextAreaState', () => {
testDeduceInput(
new TextAreaState('せn', 0, 2, null, null),
'せん',
0, 2,
0, 2, true, false,
'せん', 2
);
@@ -194,7 +194,7 @@ suite('TextAreaState', () => {
testDeduceInput(
new TextAreaState('せん', 0, 2, null, null),
'せんs',
0, 3,
0, 3, true, false,
'せんs', 2
);
@@ -204,7 +204,7 @@ suite('TextAreaState', () => {
testDeduceInput(
new TextAreaState('せんs', 0, 3, null, null),
'せんせ',
0, 3,
0, 3, true, false,
'せんせ', 3
);
@@ -214,7 +214,7 @@ suite('TextAreaState', () => {
testDeduceInput(
new TextAreaState('せんせ', 0, 3, null, null),
'せんせ',
0, 3,
0, 3, true, false,
'せんせ', 3
);
@@ -224,7 +224,7 @@ suite('TextAreaState', () => {
testDeduceInput(
new TextAreaState('せんせ', 0, 3, null, null),
'せんせい',
0, 4,
0, 4, true, false,
'せんせい', 3
);
@@ -234,7 +234,7 @@ suite('TextAreaState', () => {
testDeduceInput(
new TextAreaState('せんせい', 0, 4, null, null),
'せんせい',
4, 4,
4, 4, true, false,
'', 0
);
});
@@ -253,7 +253,7 @@ suite('TextAreaState', () => {
testDeduceInput(
new TextAreaState('せんせい', 0, 4, null, null),
'せんせい',
0, 4,
0, 4, true, false,
'せんせい', 4
);
@@ -263,7 +263,7 @@ suite('TextAreaState', () => {
testDeduceInput(
new TextAreaState('せんせい', 0, 4, null, null),
'先生',
0, 2,
0, 2, true, false,
'先生', 4
);
@@ -273,7 +273,7 @@ suite('TextAreaState', () => {
testDeduceInput(
new TextAreaState('先生', 0, 2, null, null),
'先生',
2, 2,
2, 2, true, false,
'', 0
);
});
@@ -282,7 +282,7 @@ suite('TextAreaState', () => {
testDeduceInput(
null,
'a',
0, 1,
0, 1, true, false,
'a', 0
);
});
@@ -291,7 +291,7 @@ suite('TextAreaState', () => {
testDeduceInput(
new TextAreaState(']\n', 1, 2, null, null),
']\n',
2, 2,
2, 2, true, false,
'\n', 0
);
});
@@ -300,7 +300,7 @@ suite('TextAreaState', () => {
testDeduceInput(
null,
'a',
1, 1,
1, 1, true, false,
'a', 0
);
});
@@ -309,7 +309,7 @@ suite('TextAreaState', () => {
testDeduceInput(
TextAreaState.EMPTY,
'a',
0, 1,
0, 1, true, false,
'a', 0
);
});
@@ -318,7 +318,7 @@ suite('TextAreaState', () => {
testDeduceInput(
TextAreaState.EMPTY,
'a',
1, 1,
1, 1, true, false,
'a', 0
);
});
@@ -327,7 +327,7 @@ suite('TextAreaState', () => {
testDeduceInput(
new TextAreaState('Hello world!', 0, 12, null, null),
'H',
1, 1,
1, 1, true, false,
'H', 0
);
});
@@ -336,7 +336,7 @@ suite('TextAreaState', () => {
testDeduceInput(
new TextAreaState('Hello world!', 12, 12, null, null),
'Hello world!a',
13, 13,
13, 13, true, false,
'a', 0
);
});
@@ -345,7 +345,7 @@ suite('TextAreaState', () => {
testDeduceInput(
new TextAreaState('Hello world!', 0, 0, null, null),
'aHello world!',
1, 1,
1, 1, true, false,
'a', 0
);
});
@@ -354,7 +354,7 @@ suite('TextAreaState', () => {
testDeduceInput(
new TextAreaState('Hello world!', 6, 11, null, null),
'Hello other!',
11, 11,
11, 11, true, false,
'other', 0
);
});
@@ -363,7 +363,7 @@ suite('TextAreaState', () => {
testDeduceInput(
TextAreaState.EMPTY,
'これは',
3, 3,
3, 3, true, false,
'これは', 0
);
});
@@ -372,7 +372,7 @@ suite('TextAreaState', () => {
testDeduceInput(
new TextAreaState('Hello world!', 0, 0, null, null),
'Aello world!',
1, 1,
1, 1, true, false,
'A', 0
);
});
@@ -381,7 +381,7 @@ suite('TextAreaState', () => {
testDeduceInput(
new TextAreaState('Hello world!', 5, 5, null, null),
'Hellö world!',
4, 5,
4, 5, true, false,
'ö', 0
);
});
@@ -390,7 +390,7 @@ suite('TextAreaState', () => {
testDeduceInput(
new TextAreaState('Hello world!', 5, 5, null, null),
'Hellöö world!',
5, 5,
5, 5, true, false,
'öö', 1
);
});
@@ -399,7 +399,7 @@ suite('TextAreaState', () => {
testDeduceInput(
new TextAreaState('Hello world!', 5, 5, null, null),
'Helöö world!',
5, 5,
5, 5, true, false,
'öö', 2
);
});
@@ -408,7 +408,7 @@ suite('TextAreaState', () => {
testDeduceInput(
new TextAreaState('Hello world!', 5, 5, null, null),
'Hellö world!',
5, 5,
5, 5, true, false,
'ö', 1
);
});
@@ -417,7 +417,7 @@ suite('TextAreaState', () => {
testDeduceInput(
new TextAreaState('a', 0, 1, null, null),
'a',
1, 1,
1, 1, true, false,
'a', 0
);
});
@@ -426,7 +426,7 @@ suite('TextAreaState', () => {
testDeduceInput(
new TextAreaState('x x', 0, 1, null, null),
'x x',
1, 1,
1, 1, true, false,
'x', 0
);
});
@@ -456,7 +456,7 @@ suite('TextAreaState', () => {
'some6 text',
'some7 text'
].join('\n'),
4, 4,
4, 4, true, false,
'📅', 0
);
});
@@ -470,7 +470,7 @@ suite('TextAreaState', () => {
null, null
),
'some💊1 text',
6, 6,
6, 6, true, false,
'💊', 0
);
});
@@ -484,7 +484,7 @@ suite('TextAreaState', () => {
null, null
),
'qwertyu\nasdfghj\nzxcvbnm🎈',
25, 25,
25, 25, true, false,
'🎈', 0
);
});
@@ -499,11 +499,25 @@ suite('TextAreaState', () => {
null, null
),
'some⌨️1 text',
6, 6,
6, 6, true, false,
'⌨️', 0
);
});
test('issue #42251: Minor issue, character swapped when typing', () => {
// Typing on OSX occurs at offset 0 after moving the window using the custom (non-native) titlebar.
testDeduceInput(
new TextAreaState(
'ab',
2, 2,
null, null
),
'cab',
1, 1, true, true,
'c', 0
);
});
suite('PagedScreenReaderStrategy', () => {
function testPagedScreenReaderStrategy(lines: string[], selection: Selection, expected: TextAreaState): void {
@@ -12,6 +12,7 @@ import { MetadataConsts } from 'vs/editor/common/modes';
import { LineDecoration } from 'vs/editor/common/viewLayout/lineDecorations';
import { InlineDecorationType } from 'vs/editor/common/viewModel/viewModel';
import { IViewLineTokens } from 'vs/editor/common/core/lineTokens';
import * as strings from 'vs/base/common/strings';
function createViewLineTokens(viewLineTokens: ViewLineToken[]): IViewLineTokens {
return new ViewLineTokens(viewLineTokens);
@@ -29,6 +30,7 @@ suite('viewLineRenderer.renderLine', () => {
let _actual = renderViewLine(new RenderLineInput(
false,
lineContent,
strings.isBasicASCII(lineContent),
false,
0,
createViewLineTokens([new ViewLineToken(lineContent.length, 0)]),
@@ -75,6 +77,7 @@ suite('viewLineRenderer.renderLine', () => {
let _actual = renderViewLine(new RenderLineInput(
false,
lineContent,
true,
false,
0,
createViewLineTokens(parts),
@@ -111,6 +114,7 @@ suite('viewLineRenderer.renderLine', () => {
let _actual = renderViewLine(new RenderLineInput(
false,
'Hello world!',
true,
false,
0,
createViewLineTokens([
@@ -212,6 +216,7 @@ suite('viewLineRenderer.renderLine', () => {
let _actual = renderViewLine(new RenderLineInput(
false,
lineText,
true,
false,
0,
lineParts,
@@ -271,6 +276,7 @@ suite('viewLineRenderer.renderLine', () => {
let _actual = renderViewLine(new RenderLineInput(
false,
lineText,
true,
false,
0,
lineParts,
@@ -330,6 +336,7 @@ suite('viewLineRenderer.renderLine', () => {
let _actual = renderViewLine(new RenderLineInput(
false,
lineText,
true,
false,
0,
lineParts,
@@ -366,6 +373,7 @@ suite('viewLineRenderer.renderLine', () => {
let _actual = renderViewLine(new RenderLineInput(
false,
lineText,
false,
true,
0,
lineParts,
@@ -393,6 +401,7 @@ suite('viewLineRenderer.renderLine', () => {
let actual = renderViewLine(new RenderLineInput(
false,
lineText,
true,
false,
0,
lineParts,
@@ -490,6 +499,7 @@ suite('viewLineRenderer.renderLine', () => {
let actual = renderViewLine(new RenderLineInput(
false,
lineText,
true,
false,
0,
lineParts,
@@ -524,6 +534,7 @@ suite('viewLineRenderer.renderLine', () => {
false,
lineText,
false,
false,
0,
lineParts,
[],
@@ -535,11 +546,7 @@ suite('viewLineRenderer.renderLine', () => {
false
));
let expectedOutput = [
'<span class="mtk1">a𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷</span>',
'<span class="mtk1">𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷</span>',
'<span class="mtk1">𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷</span>',
'<span class="mtk1">𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷</span>',
'<span class="mtk1">𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷</span>',
'<span class="mtk1">a𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷𠮷</span>',
];
assert.equal(actual.html, '<span>' + expectedOutput.join('') + '</span>');
});
@@ -553,6 +560,7 @@ suite('viewLineRenderer.renderLine', () => {
let actual = renderViewLine(new RenderLineInput(
false,
lineText,
false,
true,
0,
lineParts,
@@ -596,6 +604,7 @@ suite('viewLineRenderer.renderLine', () => {
let _actual = renderViewLine(new RenderLineInput(
true,
lineText,
true,
false,
4,
lineParts,
@@ -676,6 +685,7 @@ suite('viewLineRenderer.renderLine 2', () => {
let actual = renderViewLine(new RenderLineInput(
fontIsMonospace,
lineContent,
true,
false,
fauxIndentLength,
createViewLineTokens(tokens),
@@ -698,6 +708,7 @@ suite('viewLineRenderer.renderLine 2', () => {
let actual = renderViewLine(new RenderLineInput(
false,
lineContent,
true,
false,
0,
createViewLineTokens([createPart(21, 3)]),
@@ -726,6 +737,7 @@ suite('viewLineRenderer.renderLine 2', () => {
let actual = renderViewLine(new RenderLineInput(
true,
lineContent,
true,
false,
0,
createViewLineTokens([
@@ -990,6 +1002,7 @@ suite('viewLineRenderer.renderLine 2', () => {
let actual = renderViewLine(new RenderLineInput(
false,
'Hello world',
true,
false,
0,
createViewLineTokens([createPart(11, 0)]),
@@ -1031,6 +1044,7 @@ suite('viewLineRenderer.renderLine 2', () => {
let actual = renderViewLine(new RenderLineInput(
false,
lineContent,
true,
false,
0,
createViewLineTokens([createPart(4, 3)]),
@@ -1060,6 +1074,7 @@ suite('viewLineRenderer.renderLine 2', () => {
let actual = renderViewLine(new RenderLineInput(
false,
lineContent,
true,
false,
0,
createViewLineTokens([createPart(4, 3)]),
@@ -1090,6 +1105,7 @@ suite('viewLineRenderer.renderLine 2', () => {
let actual = renderViewLine(new RenderLineInput(
false,
lineContent,
true,
false,
0,
createViewLineTokens([createPart(0, 3)]),
@@ -1117,6 +1133,7 @@ suite('viewLineRenderer.renderLine 2', () => {
true,
' 1. 🙏',
false,
false,
0,
createViewLineTokens([createPart(7, 3)]),
[new LineDecoration(7, 8, 'inline-folded', InlineDecorationType.After)],
@@ -1143,6 +1160,7 @@ suite('viewLineRenderer.renderLine 2', () => {
let actual = renderViewLine(new RenderLineInput(
true,
'',
true,
false,
0,
createViewLineTokens([createPart(0, 3)]),
@@ -1172,6 +1190,7 @@ suite('viewLineRenderer.renderLine 2', () => {
let actual = renderViewLine(new RenderLineInput(
true,
'\t}',
true,
false,
0,
createViewLineTokens([createPart(2, 3)]),
@@ -1203,6 +1222,7 @@ suite('viewLineRenderer.renderLine 2', () => {
true,
'asd = "擦"\t\t#asd',
false,
false,
0,
createViewLineTokens([createPart(15, 3)]),
[],
@@ -1229,6 +1249,7 @@ suite('viewLineRenderer.renderLine 2', () => {
true,
'asd = "擦"\t\t#asd',
false,
false,
0,
createViewLineTokens([createPart(15, 3)]),
[],
@@ -1255,10 +1276,96 @@ suite('viewLineRenderer.renderLine 2', () => {
assert.deepEqual(actual.html, expected);
});
test('issue #22352: COMBINING ACUTE ACCENT (U+0301)', () => {
let actual = renderViewLine(new RenderLineInput(
true,
'12345689012345678901234568901234567890123456890abába',
false,
false,
0,
createViewLineTokens([createPart(53, 3)]),
[],
4,
10,
10000,
'none',
false,
false
));
let expected = [
'<span>',
'<span class="mtk3">12345689012345678901234568901234567890123456890abába</span>',
'</span>'
].join('');
assert.deepEqual(actual.html, expected);
});
test('issue #22352: Partially Broken Complex Script Rendering of Tamil', () => {
let actual = renderViewLine(new RenderLineInput(
true,
' JoyShareல் பின்தொடர்ந்து, விடீயோ, ஜோக்குகள், அனிமேசன், நகைச்சுவை படங்கள் மற்றும் செய்திகளை பெறுவீர்',
false,
false,
0,
createViewLineTokens([createPart(100, 3)]),
[],
4,
10,
10000,
'none',
false,
false
));
let expected = [
'<span>',
'<span class="mtk3">\u00a0JoyShareல்\u00a0பின்தொடர்ந்து,\u00a0விடீயோ,\u00a0ஜோக்குகள்,\u00a0அனிமேசன்,\u00a0நகைச்சுவை\u00a0படங்கள்\u00a0மற்றும்\u00a0செய்திகளை\u00a0பெறுவீர்</span>',
'</span>'
].join('');
let _expected = expected.split('').map(c => c.charCodeAt(0));
let _actual = actual.html.split('').map(c => c.charCodeAt(0));
assert.deepEqual(_actual, _expected);
assert.deepEqual(actual.html, expected);
});
test('issue #42700: Hindi characters are not being rendered properly', () => {
let actual = renderViewLine(new RenderLineInput(
true,
' वो ऐसा क्या है जो हमारे अंदर भी है और बाहर भी है। जिसकी वजह से हम सब हैं। जिसने इस सृष्टि की रचना की है।',
false,
false,
0,
createViewLineTokens([createPart(105, 3)]),
[],
4,
10,
10000,
'none',
false,
false
));
let expected = [
'<span>',
'<span class="mtk3">\u00a0वो\u00a0ऐसा\u00a0क्या\u00a0है\u00a0जो\u00a0हमारे\u00a0अंदर\u00a0भी\u00a0है\u00a0और\u00a0बाहर\u00a0भी\u00a0है।\u00a0जिसकी\u00a0वजह\u00a0से\u00a0हम\u00a0सब\u00a0हैं।\u00a0जिसने\u00a0इस\u00a0सृष्टि\u00a0की\u00a0रचना\u00a0की\u00a0है।</span>',
'</span>'
].join('');
assert.deepEqual(actual.html, expected);
});
function createTestGetColumnOfLinePartOffset(lineContent: string, tabSize: number, parts: ViewLineToken[], expectedPartLengths: number[]): (partIndex: number, partLength: number, offset: number, expected: number) => void {
let renderLineOutput = renderViewLine(new RenderLineInput(
false,
lineContent,
true,
false,
0,
createViewLineTokens(parts),
+9
View File
@@ -1193,6 +1193,11 @@ declare namespace monaco.editor {
* Should the decoration expand to encompass a whole line.
*/
isWholeLine?: boolean;
/**
* Specifies the stack order of a decoration.
* A decoration with greater stack order is always in front of a decoration with a lower stack order.
*/
zIndex?: number;
/**
* If set, render this decoration in the overview ruler.
*/
@@ -2191,6 +2196,10 @@ declare namespace monaco.editor {
* The range that got replaced.
*/
readonly range: IRange;
/**
* The offset of the range that got replaced.
*/
readonly rangeOffset: number;
/**
* The length of the range that got replaced.
*/
@@ -11,7 +11,6 @@ import { Registry } from 'vs/platform/registry/common/platform';
import * as types from 'vs/base/common/types';
import * as strings from 'vs/base/common/strings';
import { IJSONContributionRegistry, Extensions as JSONExtensions } from 'vs/platform/jsonschemas/common/jsonContributionRegistry';
import { deepClone } from 'vs/base/common/objects';
export const Extensions = {
Configuration: 'base.contributions.configuration'
@@ -63,8 +62,9 @@ export interface IConfigurationRegistry {
}
export enum ConfigurationScope {
WINDOW = 1,
RESOURCE
APPLICATION = 1,
WINDOW,
RESOURCE,
}
export interface IConfigurationPropertySchema extends IJSONSchema {
@@ -93,8 +93,10 @@ export interface IDefaultConfigurationExtension {
defaults: { [key: string]: {} };
}
export const settingsSchema: IJSONSchema = { properties: {}, patternProperties: {}, additionalProperties: false, errorMessage: 'Unknown configuration setting' };
export const resourceSettingsSchema: IJSONSchema = { properties: {}, patternProperties: {}, additionalProperties: false, errorMessage: 'Unknown configuration setting' };
export const allSettings: { properties: {}, patternProperties: {} } = { properties: {}, patternProperties: {} };
export const applicationSettings: { properties: {}, patternProperties: {} } = { properties: {}, patternProperties: {} };
export const windowSettings: { properties: {}, patternProperties: {} } = { properties: {}, patternProperties: {} };
export const resourceSettings: { properties: {}, patternProperties: {} } = { properties: {}, patternProperties: {} };
export const editorConfigurationSchemaId = 'vscode://schemas/settings/editor';
const contributionRegistry = Registry.as<IJSONContributionRegistry>(JSONExtensions.JSONContribution);
@@ -239,10 +241,17 @@ class ConfigurationRegistry implements IConfigurationRegistry {
let properties = configuration.properties;
if (properties) {
for (let key in properties) {
settingsSchema.properties[key] = properties[key];
resourceSettingsSchema.properties[key] = deepClone(properties[key]);
if (properties[key].scope !== ConfigurationScope.RESOURCE) {
resourceSettingsSchema.properties[key].doNotSuggest = true;
allSettings.properties[key] = properties[key];
switch (properties[key].scope) {
case ConfigurationScope.APPLICATION:
applicationSettings.properties[key] = properties[key];
break;
case ConfigurationScope.WINDOW:
windowSettings.properties[key] = properties[key];
break;
case ConfigurationScope.RESOURCE:
resourceSettings.properties[key] = properties[key];
break;
}
}
}
@@ -262,7 +271,7 @@ class ConfigurationRegistry implements IConfigurationRegistry {
}
private updateOverridePropertyPatternKey(): void {
let patternProperties: IJSONSchema = settingsSchema.patternProperties[this.overridePropertyPattern];
let patternProperties: IJSONSchema = allSettings.patternProperties[this.overridePropertyPattern];
if (!patternProperties) {
patternProperties = {
type: 'object',
@@ -271,11 +280,18 @@ class ConfigurationRegistry implements IConfigurationRegistry {
$ref: editorConfigurationSchemaId
};
}
delete settingsSchema.patternProperties[this.overridePropertyPattern];
delete allSettings.patternProperties[this.overridePropertyPattern];
delete applicationSettings.patternProperties[this.overridePropertyPattern];
delete windowSettings.patternProperties[this.overridePropertyPattern];
delete resourceSettings.patternProperties[this.overridePropertyPattern];
this.computeOverridePropertyPattern();
settingsSchema.patternProperties[this.overridePropertyPattern] = patternProperties;
resourceSettingsSchema.patternProperties[this.overridePropertyPattern] = patternProperties;
allSettings.patternProperties[this.overridePropertyPattern] = patternProperties;
applicationSettings.patternProperties[this.overridePropertyPattern] = patternProperties;
windowSettings.patternProperties[this.overridePropertyPattern] = patternProperties;
resourceSettings.patternProperties[this.overridePropertyPattern] = patternProperties;
}
private update(configuration: IConfigurationNode): void {
@@ -78,7 +78,13 @@ export class ExtensionEnablementService implements IExtensionEnablementService {
}
canChangeEnablement(extension: ILocalExtension): boolean {
return !this.environmentService.disableExtensions && !(extension.manifest && extension.manifest.contributes && extension.manifest.contributes.localizations && extension.manifest.contributes.localizations.length);
if (extension.manifest && extension.manifest.contributes && extension.manifest.contributes.localizations && extension.manifest.contributes.localizations.length) {
return false;
}
if (extension.type === LocalExtensionType.User && this.environmentService.disableExtensions) {
return false;
}
return true;
}
setEnablement(arg: ILocalExtension | IExtensionIdentifier, newState: EnablementState): TPromise<boolean> {
@@ -22,7 +22,7 @@ import {
IExtensionIdentifier,
IReportedExtension
} from 'vs/platform/extensionManagement/common/extensionManagement';
import { getGalleryExtensionIdFromLocal, adoptToGalleryExtensionId, areSameExtensions, getGalleryExtensionId, groupByExtension, getMaliciousExtensionsSet, getLocalExtensionId } from 'vs/platform/extensionManagement/common/extensionManagementUtil';
import { getGalleryExtensionIdFromLocal, adoptToGalleryExtensionId, areSameExtensions, getGalleryExtensionId, groupByExtension, getMaliciousExtensionsSet, getLocalExtensionId, getGalleryExtensionTelemetryData, getLocalExtensionTelemetryData } from 'vs/platform/extensionManagement/common/extensionManagementUtil';
import { localizeManifest } from '../common/extensionNls';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { Limiter, always } from 'vs/base/common/async';
@@ -37,6 +37,7 @@ import { IDialogService } from 'vs/platform/dialogs/common/dialogs';
import Severity from 'vs/base/common/severity';
import { ExtensionsLifecycle } from 'vs/platform/extensionManagement/node/extensionLifecycle';
import { toErrorMessage } from 'vs/base/common/errorMessage';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
const SystemExtensionsRoot = path.normalize(path.join(URI.parse(require.toUrl('')).fsPath, '..', 'extensions'));
const ERROR_SCANNING_SYS_EXTENSIONS = 'scanningSystem';
@@ -48,8 +49,9 @@ const INSTALL_ERROR_VALIDATING = 'validating';
const INSTALL_ERROR_GALLERY = 'gallery';
const INSTALL_ERROR_LOCAL = 'local';
const INSTALL_ERROR_EXTRACTING = 'extracting';
const INSTALL_ERROR_RENAMING = 'renaming';
const INSTALL_ERROR_DELETING = 'deleting';
const INSTALL_ERROR_UNKNOWN = 'unknown';
const ERROR_UNKNOWN = 'unknown';
export class ExtensionManagementError extends Error {
constructor(message: string, readonly code: string) {
@@ -108,6 +110,7 @@ export class ExtensionManagementService extends Disposable implements IExtension
private uninstalledFileLimiter: Limiter<void>;
private reportedExtensions: TPromise<IReportedExtension[]> | undefined;
private lastReportTimestamp = 0;
private readonly installationStartTime: Map<string, number> = new Map<string, number>();
private readonly installingExtensions: Map<string, TPromise<ILocalExtension>> = new Map<string, TPromise<ILocalExtension>>();
private readonly manifestCache: ExtensionsManifestCache;
private readonly extensionLifecycle: ExtensionsLifecycle;
@@ -128,7 +131,8 @@ export class ExtensionManagementService extends Disposable implements IExtension
@IEnvironmentService environmentService: IEnvironmentService,
@IDialogService private dialogService: IDialogService,
@IExtensionGalleryService private galleryService: IExtensionGalleryService,
@ILogService private logService: ILogService
@ILogService private logService: ILogService,
@ITelemetryService private telemetryService: ITelemetryService,
) {
super();
this.extensionsPath = environmentService.extensionsPath;
@@ -326,6 +330,7 @@ export class ExtensionManagementService extends Disposable implements IExtension
private onInstallExtensions(extensions: IGalleryExtension[]): void {
for (const extension of extensions) {
this.logService.info('Installing extension:', extension.name);
this.installationStartTime.set(extension.identifier.id, new Date().getTime());
const id = getLocalExtensionIdFromGallery(extension, extension.version);
this._onInstallExtension.fire({ identifier: { id, uuid: extension.identifier.uuid }, gallery: extension });
}
@@ -340,10 +345,13 @@ export class ExtensionManagementService extends Disposable implements IExtension
this.logService.info(`Extensions installed successfully:`, gallery.identifier.id);
this._onDidInstallExtension.fire({ identifier, gallery, local });
} else {
const errorCode = error && (<ExtensionManagementError>error).code ? (<ExtensionManagementError>error).code : INSTALL_ERROR_UNKNOWN;
const errorCode = error && (<ExtensionManagementError>error).code ? (<ExtensionManagementError>error).code : ERROR_UNKNOWN;
this.logService.error(`Failed to install extension:`, gallery.identifier.id, error ? error.message : errorCode);
this._onDidInstallExtension.fire({ identifier, gallery, error: errorCode });
}
const startTime = this.installationStartTime.get(gallery.identifier.id);
this.reportTelemetry('extensionGallery:install', getGalleryExtensionTelemetryData(gallery), startTime ? new Date().getTime() - startTime : void 0, error);
this.installationStartTime.delete(gallery.identifier.id);
});
return errors.length ? TPromise.wrapError(this.joinErrors(errors)) : TPromise.as(null);
}
@@ -419,7 +427,7 @@ export class ExtensionManagementService extends Disposable implements IExtension
private extractAndRename(id: string, zipPath: string, extractPath: string, renamePath: string): TPromise<void> {
return this.extract(id, zipPath, extractPath)
.then(() => this.rename(id, extractPath, renamePath, Date.now() + (20 * 1000) /* Retry for 20 seconds */)
.then(() => this.rename(id, extractPath, renamePath, Date.now() + (30 * 1000) /* Retry for 30 seconds */)
.then(
() => this.logService.info('Renamed to', renamePath),
e => {
@@ -445,7 +453,7 @@ export class ExtensionManagementService extends Disposable implements IExtension
.then(null, error =>
isWindows && error && error.code === 'EPERM' && Date.now() < retryUntil
? this.rename(id, extractPath, renamePath, retryUntil)
: TPromise.wrapError(error)
: TPromise.wrapError(new ExtensionManagementError(error.message || nls.localize('renameError', "Unknown error while"), error.code || INSTALL_ERROR_RENAMING))
);
}
@@ -524,7 +532,7 @@ export class ExtensionManagementService extends Disposable implements IExtension
.then(() => this.hasDependencies(extension, installed) ? this.promptForDependenciesAndUninstall(extension, installed, force) : this.promptAndUninstall(extension, installed, force))
.then(() => this.postUninstallExtension(extension),
error => {
this.postUninstallExtension(extension, INSTALL_ERROR_LOCAL);
this.postUninstallExtension(extension, new ExtensionManagementError(error instanceof Error ? error.message : error, INSTALL_ERROR_LOCAL));
return TPromise.wrapError(error);
});
}
@@ -644,7 +652,7 @@ export class ExtensionManagementService extends Disposable implements IExtension
.then(() => this.uninstallExtension(extension))
.then(() => this.postUninstallExtension(extension),
error => {
this.postUninstallExtension(extension, INSTALL_ERROR_LOCAL);
this.postUninstallExtension(extension, new ExtensionManagementError(error instanceof Error ? error.message : error, INSTALL_ERROR_LOCAL));
return TPromise.wrapError(error);
});
}
@@ -664,9 +672,9 @@ export class ExtensionManagementService extends Disposable implements IExtension
.then(userExtensions => this.setUninstalled(...userExtensions.filter(u => areSameExtensions({ id: getGalleryExtensionIdFromLocal(u), uuid: u.identifier.uuid }, { id: getGalleryExtensionIdFromLocal(local), uuid: local.identifier.uuid }))));
}
private async postUninstallExtension(extension: ILocalExtension, error?: string): TPromise<void> {
private async postUninstallExtension(extension: ILocalExtension, error?: Error): TPromise<void> {
if (error) {
this.logService.error('Failed to uninstall extension:', extension.identifier.id, error);
this.logService.error('Failed to uninstall extension:', extension.identifier.id, error.message);
} else {
this.logService.info('Successfully uninstalled extension:', extension.identifier.id);
// only report if extension has a mapped gallery extension. UUID identifies the gallery extension.
@@ -674,7 +682,9 @@ export class ExtensionManagementService extends Disposable implements IExtension
await this.galleryService.reportStatistic(extension.manifest.publisher, extension.manifest.name, extension.manifest.version, StatisticType.Uninstall);
}
}
this._onDidUninstallExtension.fire({ identifier: extension.identifier, error });
this.reportTelemetry('extensionGallery:uninstall', getLocalExtensionTelemetryData(extension), void 0, error);
const errorcode = error ? error instanceof ExtensionManagementError ? error.code : ERROR_UNKNOWN : void 0;
this._onDidUninstallExtension.fire({ identifier: extension.identifier, error: errorcode });
}
getInstalled(type: LocalExtensionType = null): TPromise<ILocalExtension[]> {
@@ -852,6 +862,31 @@ export class ExtensionManagementService extends Disposable implements IExtension
return [];
});
}
private reportTelemetry(eventName: string, extensionData: any, duration: number, error?: Error): void {
const errorcode = error ? error instanceof ExtensionManagementError ? error.code : ERROR_UNKNOWN : void 0;
/* __GDPR__
"extensionGallery:install" : {
"success": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true },
"duration" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true },
"errorcode": { "classification": "CallstackOrException", "purpose": "PerformanceAndHealth" },
"${include}": [
"${GalleryExtensionTelemetryData}"
]
}
*/
/* __GDPR__
"extensionGallery:uninstall" : {
"success": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true },
"duration" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true },
"errorcode": { "classification": "CallstackOrException", "purpose": "PerformanceAndHealth" },
"${include}": [
"${GalleryExtensionTelemetryData}"
]
}
*/
this.telemetryService.publicLog(eventName, assign(extensionData, { success: !error, duration, errorcode }));
}
}
export function getLocalExtensionIdFromGallery(extension: IGalleryExtension, version: string): string {
@@ -6,7 +6,7 @@
import * as assert from 'assert';
import * as sinon from 'sinon';
import { IExtensionManagementService, IExtensionEnablementService, DidUninstallExtensionEvent, EnablementState, IExtensionContributions, ILocalExtension } from 'vs/platform/extensionManagement/common/extensionManagement';
import { IExtensionManagementService, IExtensionEnablementService, DidUninstallExtensionEvent, EnablementState, IExtensionContributions, ILocalExtension, LocalExtensionType } from 'vs/platform/extensionManagement/common/extensionManagement';
import { ExtensionEnablementService } from 'vs/platform/extensionManagement/common/extensionEnablementService';
import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock';
import { Emitter } from 'vs/base/common/event';
@@ -324,6 +324,20 @@ suite('ExtensionEnablementService Test', () => {
test('test canChangeEnablement return false for language packs', () => {
assert.equal(testObject.canChangeEnablement(aLocalExtension('pub.a', { localizations: [{ languageId: 'gr', translations: [{ id: 'vscode', path: 'path' }] }] })), false);
});
test('test canChangeEnablement return false when extensions are disabled in environment', () => {
instantiationService.stub(IEnvironmentService, { disableExtensions: true } as IEnvironmentService);
testObject = new TestExtensionEnablementService(instantiationService);
assert.equal(testObject.canChangeEnablement(aLocalExtension('pub.a')), false);
});
test('test canChangeEnablement return true for system extensions when extensions are disabled in environment', () => {
instantiationService.stub(IEnvironmentService, { disableExtensions: true } as IEnvironmentService);
testObject = new TestExtensionEnablementService(instantiationService);
const extension = aLocalExtension('pub.a');
extension.type = LocalExtensionType.System;
assert.equal(testObject.canChangeEnablement(extension), true);
});
});
function aLocalExtension(id: string, contributes?: IExtensionContributions): ILocalExtension {
@@ -334,6 +348,7 @@ function aLocalExtension(id: string, contributes?: IExtensionContributions): ILo
name,
publisher,
contributes
}
},
type: LocalExtensionType.User
});
}
@@ -14,7 +14,7 @@ import URI from 'vs/base/common/uri';
import Severity from 'vs/base/common/severity';
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
import { ILifecycleService, LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle';
import { INotificationService, PromptOption } from 'vs/platform/notification/common/notification';
import { INotificationService } from 'vs/platform/notification/common/notification';
interface IStorageData {
dontShowPrompt: boolean;
@@ -82,26 +82,24 @@ export class IntegrityServiceImpl implements IIntegrityService {
private _prompt(): void {
const storedData = this._storage.get();
if (storedData && storedData.dontShowPrompt && storedData.commit === product.commit) {
// Do not prompt
return;
return; // Do not prompt
}
const choices: PromptOption[] = [nls.localize('integrity.moreInformation', "More Information"), { label: nls.localize('integrity.dontShowAgain', "Don't Show Again") }];
this.notificationService.prompt(Severity.Warning, nls.localize('integrity.prompt', "Your {0} installation appears to be corrupt. Please reinstall.", product.nameShort), choices).then(choice => {
switch (choice) {
case 0 /* More Information */:
const uri = URI.parse(product.checksumFailMoreInfoUrl);
window.open(uri.toString(true));
break;
case 1 /* Do not show again */:
this._storage.set({
dontShowPrompt: true,
commit: product.commit
});
break;
}
});
this.notificationService.prompt(
Severity.Warning,
nls.localize('integrity.prompt', "Your {0} installation appears to be corrupt. Please reinstall.", product.nameShort),
[
{
label: nls.localize('integrity.moreInformation', "More Information"),
run: () => window.open(URI.parse(product.checksumFailMoreInfoUrl).toString(true))
},
{
label: nls.localize('integrity.dontShowAgain', "Don't Show Again"),
isSecondary: true,
run: () => this._storage.set({ dontShowPrompt: true, commit: product.commit })
}
]
);
}
public isPure(): Thenable<IntegrityTestResult> {
@@ -19,7 +19,7 @@ import { ResolvedKeybindingItem } from 'vs/platform/keybinding/common/resolvedKe
import { OS } from 'vs/base/common/platform';
import { IKeyboardEvent } from 'vs/platform/keybinding/common/keybinding';
import { NullTelemetryService } from 'vs/platform/telemetry/common/telemetryUtils';
import { INotificationService, NoOpNotification, INotification } from 'vs/platform/notification/common/notification';
import { INotificationService, NoOpNotification, INotification, IPromptChoice } from 'vs/platform/notification/common/notification';
function createContext(ctx: any) {
return {
@@ -139,8 +139,8 @@ suite('AbstractKeybindingService', () => {
showMessageCalls.push({ sev: Severity.Error, message });
return new NoOpNotification();
},
prompt: () => {
return TPromise.as(0);
prompt(severity: Severity, message: string, choices: IPromptChoice[], onCancel?: () => void) {
throw new Error('not implemented');
}
};
@@ -7,10 +7,8 @@
import BaseSeverity from 'vs/base/common/severity';
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
import { IDisposable } from 'vs/base/common/lifecycle';
import { IAction } from 'vs/base/common/actions';
import { Event, Emitter } from 'vs/base/common/event';
import { TPromise } from 'vs/base/common/winjs.base';
export import Severity = BaseSeverity;
@@ -90,12 +88,12 @@ export interface INotificationProgress {
done(): void;
}
export interface INotificationHandle extends IDisposable {
export interface INotificationHandle {
/**
* Will be fired once the notification is disposed.
* Will be fired once the notification is closed.
*/
readonly onDidDispose: Event<void>;
readonly onDidClose: Event<void>;
/**
* Allows to indicate progress on the notification even after the
@@ -119,28 +117,37 @@ export interface INotificationHandle extends IDisposable {
* notification is already visible.
*/
updateActions(actions?: INotificationActions): void;
/**
* Hide the notification and remove it from the notification center.
*/
close(): void;
}
export interface IPromptChoice {
/**
* Primary choices show up as buttons in the notification below the message.
*/
export type PrimaryPromptChoice = string;
/**
* Secondary choices show up under the gear icon in the header of the notification.
*/
export interface SecondaryPromptChoice {
/**
* Label to show for the choice to the user.
*/
label: string;
/**
* Wether to keep the notification open after the secondary choice was selected
* Primary choices show up as buttons in the notification below the message.
* Secondary choices show up under the gear icon in the header of the notification.
*/
isSecondary?: boolean;
/**
* Wether to keep the notification open after the choice was selected
* by the user. By default, will close the notification upon click.
*/
keepOpen?: boolean;
}
export type PromptOption = PrimaryPromptChoice | SecondaryPromptChoice;
/**
* Triggered when the user selects the choice.
*/
run: () => void;
}
/**
* A service to bring up notifications and non-modal prompts.
@@ -185,27 +192,29 @@ export interface INotificationService {
* Shows a prompt in the notification area with the provided choices. The prompt
* is non-modal. If you want to show a modal dialog instead, use `IDialogService`.
*
* @returns a promise that will resolve to the index of the choice that was picked.
* The promise can be cancelled to hide the notification prompt.
* @param onCancel will be called if the user closed the notification without picking
* any of the provided choices.
*
* @returns a handle on the notification to e.g. hide it or update message, buttons, etc.
*/
prompt(severity: Severity, message: string, choices: PromptOption[]): TPromise<number>;
prompt(severity: Severity, message: string, choices: IPromptChoice[], onCancel?: () => void): INotificationHandle;
}
export class NoOpNotification implements INotificationHandle {
readonly progress = new NoOpProgress();
private readonly _onDidDispose: Emitter<void> = new Emitter();
private readonly _onDidClose: Emitter<void> = new Emitter();
public get onDidDispose(): Event<void> {
return this._onDidDispose.event;
public get onDidClose(): Event<void> {
return this._onDidClose.event;
}
updateSeverity(severity: Severity): void { }
updateMessage(message: NotificationMessage): void { }
updateActions(actions?: INotificationActions): void { }
dispose(): void {
this._onDidDispose.dispose();
close(): void {
this._onDidClose.dispose();
}
}
@@ -130,15 +130,16 @@ export class TelemetryService implements ITelemetryService {
}
}
const fileRegex = /(file:\/\/)?([a-z,A-Z]:)?([\\\/]\w+)+/g;
const nodeModulesRegex = /^[\\\/]?(node_modules|node_modules\.asar)[\\\/]/;
const fileRegex = /(file:\/\/)?([a-zA-Z]:(\\\\|\\|\/)|(\\\\|\\|\/))?([\w-\._]+(\\\\|\\|\/))+[\w-\._]*/g;
let updatedStack = stack;
while (true) {
const result = fileRegex.exec(stack);
if (!result) {
break;
}
// Anoynimize user file paths that do not need cleanup.
if (cleanUpIndexes.every(([x, y]) => result.index < x || result.index >= y)) {
// Anoynimize user file paths that do not need to be retained or cleaned up.
if (!nodeModulesRegex.test(result[0]) && cleanUpIndexes.every(([x, y]) => result.index < x || result.index >= y)) {
updatedStack = updatedStack.slice(0, result.index) + result[0].replace(/./g, 'a') + updatedStack.slice(fileRegex.lastIndex);
}
}
@@ -50,8 +50,10 @@ class ErrorTestingSettings {
public noSuchFilePrefix: string;
public noSuchFileMessage: string;
public stack: string[];
public randomUserFile: string = 'a/path/that/doesnt/contain/code/names';
public anonymizedRandomUserFile: string = 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa';
public randomUserFile: string = 'a/path/that/doe_snt/con-tain/code/names.js';
public anonymizedRandomUserFile: string = 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa';
public nodeModulePathToRetain: string = 'node_modules/path/that/shouldbe/retained/names.js:14:15854';
public nodeModuleAsarPathToRetain: string = 'node_modules.asar/path/that/shouldbe/retained/names.js:14:12354';
constructor() {
this.personalInfo = 'DANGEROUS/PATH';
@@ -66,16 +68,16 @@ class ErrorTestingSettings {
this.noSuchFilePrefix = 'ENOENT: no such file or directory';
this.noSuchFileMessage = this.noSuchFilePrefix + ' \'' + this.personalInfo + '\'';
this.stack = [`at e._modelEvents (${this.randomUserFile}.js:11:7309)`,
` at t.AllWorkers (${this.randomUserFile}.js:6:8844)`,
` at e.(anonymous function) [as _modelEvents] (${this.randomUserFile}.js:5:29552)`,
` at Function.<anonymous> (${this.randomUserFile}.js:6:8272)`,
` at e.dispatch (${this.randomUserFile}.js:5:26931)`,
` at e.request (${this.randomUserFile}.js:14:1745)`,
' at t._handleMessage (another/path/that/doesnt/contain/code/names.js:14:17447)',
' at t._onmessage (another/path/that/doesnt/contain/code/names.js:14:16976)',
' at t.onmessage (another/path/that/doesnt/contain/code/names.js:14:15854)',
' at DedicatedWorkerGlobalScope.self.onmessage',
this.stack = [`at e._modelEvents (${this.randomUserFile}:11:7309)`,
` at t.AllWorkers (${this.randomUserFile}:6:8844)`,
` at e.(anonymous function) [as _modelEvents] (${this.randomUserFile}:5:29552)`,
` at Function.<anonymous> (${this.randomUserFile}:6:8272)`,
` at e.dispatch (${this.randomUserFile}:5:26931)`,
` at e.request (/${this.nodeModuleAsarPathToRetain})`,
` at t._handleMessage (${this.nodeModuleAsarPathToRetain})`,
` at t._onmessage (/${this.nodeModulePathToRetain})`,
` at t.onmessage (${this.nodeModulePathToRetain})`,
` at DedicatedWorkerGlobalScope.self.onmessage`,
this.dangerousPathWithImportantInfo,
this.dangerousPathWithoutImportantInfo,
this.missingModelMessage,
@@ -461,6 +463,62 @@ suite('TelemetryService', () => {
service.dispose();
}));
test('Unexpected Error Telemetry removes PII but preserves Code file path with node modules', sinon.test(function (this: any) {
let origErrorHandler = Errors.errorHandler.getUnexpectedErrorHandler();
Errors.setUnexpectedErrorHandler(() => { });
try {
let settings = new ErrorTestingSettings();
let testAppender = new TestTelemetryAppender();
let service = new TelemetryService({ appender: testAppender }, undefined);
const errorTelemetry = new ErrorTelemetry(service);
let dangerousPathWithImportantInfoError: any = new Error(settings.dangerousPathWithImportantInfo);
dangerousPathWithImportantInfoError.stack = settings.stack;
Errors.onUnexpectedError(dangerousPathWithImportantInfoError);
this.clock.tick(ErrorTelemetry.ERROR_FLUSH_TIMEOUT);
assert.notEqual(testAppender.events[0].data.stack.indexOf('(' + settings.nodeModuleAsarPathToRetain), -1);
assert.notEqual(testAppender.events[0].data.stack.indexOf('(' + settings.nodeModulePathToRetain), -1);
assert.notEqual(testAppender.events[0].data.stack.indexOf('(/' + settings.nodeModuleAsarPathToRetain), -1);
assert.notEqual(testAppender.events[0].data.stack.indexOf('(/' + settings.nodeModulePathToRetain), -1);
errorTelemetry.dispose();
service.dispose();
}
finally {
Errors.setUnexpectedErrorHandler(origErrorHandler);
}
}));
test('Uncaught Error Telemetry removes PII but preserves Code file path', sinon.test(function (this: any) {
let errorStub = sinon.stub();
window.onerror = errorStub;
let settings = new ErrorTestingSettings();
let testAppender = new TestTelemetryAppender();
let service = new TelemetryService({ appender: testAppender }, undefined);
const errorTelemetry = new ErrorTelemetry(service);
let dangerousPathWithImportantInfoError: any = new Error('dangerousPathWithImportantInfo');
dangerousPathWithImportantInfoError.stack = settings.stack;
(<any>window.onerror)(settings.dangerousPathWithImportantInfo, 'test.js', 2, 42, dangerousPathWithImportantInfoError);
this.clock.tick(ErrorTelemetry.ERROR_FLUSH_TIMEOUT);
assert.equal(errorStub.callCount, 1);
assert.notEqual(testAppender.events[0].data.stack.indexOf('(' + settings.nodeModuleAsarPathToRetain), -1);
assert.notEqual(testAppender.events[0].data.stack.indexOf('(' + settings.nodeModulePathToRetain), -1);
assert.notEqual(testAppender.events[0].data.stack.indexOf('(/' + settings.nodeModuleAsarPathToRetain), -1);
assert.notEqual(testAppender.events[0].data.stack.indexOf('(/' + settings.nodeModulePathToRetain), -1);
errorTelemetry.dispose();
service.dispose();
}));
test('Unexpected Error Telemetry removes PII but preserves Code file path when PIIPath is configured', sinon.test(function (this: any) {
let origErrorHandler = Errors.errorHandler.getUnexpectedErrorHandler();
@@ -7,7 +7,7 @@
import * as nls from 'vs/nls';
import { Registry } from 'vs/platform/registry/common/platform';
import { IConfigurationRegistry, Extensions as ConfigurationExtensions } from 'vs/platform/configuration/common/configurationRegistry';
import { IConfigurationRegistry, Extensions as ConfigurationExtensions, ConfigurationScope } from 'vs/platform/configuration/common/configurationRegistry';
const configurationRegistry = Registry.as<IConfigurationRegistry>(ConfigurationExtensions.Configuration);
configurationRegistry.registerConfiguration({
@@ -20,6 +20,7 @@ configurationRegistry.registerConfiguration({
'type': 'string',
'enum': ['none', 'default'],
'default': 'default',
'scope': ConfigurationScope.APPLICATION,
'description': nls.localize('updateChannel', "Configure whether you receive automatic updates from an update channel. Requires a restart after change.")
},
'update.enableWindowsBackgroundUpdates': {
+4
View File
@@ -5511,6 +5511,10 @@ declare module 'vscode' {
* The range that got replaced.
*/
range: Range;
/**
* The offset of the range that got replaced.
*/
rangeOffset: number;
/**
* The length of the range that got replaced.
*/
+43 -2
View File
@@ -568,7 +568,7 @@ declare module 'vscode' {
*/
export interface Webview {
/**
* The type of the webview, such as `'markdownw.preview'`
* The type of the webview, such as `'markdown.preview'`
*/
readonly viewType: string;
@@ -636,16 +636,57 @@ declare module 'vscode' {
dispose(): any;
}
/**
* Save and restore webviews that have been persisted when vscode shuts down.
*/
interface WebviewSerializer {
/**
* Save a webview's `state`.
*
* Called before shutdown. Webview may or may not be visible.
*
* @param webview Webview to serialize.
*
* @returns JSON serializable state blob.
*/
serializeWebview(webview: Webview): Thenable<any>;
/**
* Restore a webview from its `state`.
*
* Called when a serialized webview first becomes active.
*
* @param webview Webview to restore. The serializer should take ownership of this webview.
* @param state Persisted state.
*
* @return Was deserialization successful?
*/
deserializeWebview(webview: Webview, state: any): Thenable<boolean>;
}
namespace window {
/**
* Create and show a new webview.
*
* @param viewType Identifier the type of the webview.
* @param viewType Identifies the type of the webview.
* @param title Title of the webview.
* @param column Editor column to show the new webview in.
* @param options Content settings for the webview.
*/
export function createWebview(viewType: string, title: string, column: ViewColumn, options: WebviewOptions): Webview;
/**
* Registers a webview serializer.
*
* Extensions that support reviving should have an `"onView:viewType"` activation method and
* make sure that `registerWebviewSerializer` is called during activation.
*
* Only a single serializer may be registered at a time for a given `viewType`.
*
* @param viewType Type of the webview that can be serialized.
* @param reviver Webview serializer.
*/
export function registerWebviewSerializer(viewType: string, reviver: WebviewSerializer): Disposable;
}
//#endregion
@@ -92,7 +92,7 @@ export class MainThreadMessageService implements MainThreadMessageServiceShape {
// if promise has not been resolved yet, now is the time to ensure a return value
// otherwise if already resolved it means the user clicked one of the buttons
once(messageHandle.onDidDispose)(() => {
once(messageHandle.onDidClose)(() => {
dispose(...primaryActions, ...secondaryActions);
resolve(undefined);
});
@@ -2,44 +2,58 @@
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import * as map from 'vs/base/common/map';
import { MainThreadWebviewsShape, MainContext, IExtHostContext, ExtHostContext, ExtHostWebviewsShape, WebviewHandle } from 'vs/workbench/api/node/extHost.protocol';
import { dispose, Disposable } from 'vs/base/common/lifecycle';
import { extHostNamedCustomer } from './extHostCustomers';
import { Position } from 'vs/platform/editor/common/editor';
import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService';
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
import { IPartService } from 'vs/workbench/services/part/common/partService';
import { IOpenerService } from 'vs/platform/opener/common/opener';
import * as vscode from 'vscode';
import { IEditorGroupService } from 'vs/workbench/services/group/common/groupService';
import URI from 'vs/base/common/uri';
import { WebviewInput } from 'vs/workbench/parts/webview/electron-browser/webviewInput';
import { TPromise } from 'vs/base/common/winjs.base';
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
import { Position } from 'vs/platform/editor/common/editor';
import { ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle';
import { IOpenerService } from 'vs/platform/opener/common/opener';
import { ExtHostContext, ExtHostWebviewsShape, IExtHostContext, MainContext, MainThreadWebviewsShape, WebviewHandle } from 'vs/workbench/api/node/extHost.protocol';
import { WebviewEditor } from 'vs/workbench/parts/webview/electron-browser/webviewEditor';
import { WebviewEditorInput } from 'vs/workbench/parts/webview/electron-browser/webviewInput';
import { IWebviewService, WebviewInputOptions, WebviewReviver } from 'vs/workbench/parts/webview/electron-browser/webviewService';
import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService';
import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
import { IEditorGroupService } from 'vs/workbench/services/group/common/groupService';
import { extHostNamedCustomer } from './extHostCustomers';
@extHostNamedCustomer(MainContext.MainThreadWebviews)
export class MainThreadWebviews implements MainThreadWebviewsShape {
export class MainThreadWebviews implements MainThreadWebviewsShape, WebviewReviver {
private static readonly viewType = 'mainThreadWebview';
private static readonly standardSupportedLinkSchemes = ['http', 'https', 'mailto'];
private _toDispose: Disposable[] = [];
private static revivalPool = 0;
private _toDispose: IDisposable[] = [];
private readonly _proxy: ExtHostWebviewsShape;
private readonly _webviews = new Map<WebviewHandle, WebviewInput>();
private readonly _webviews = new Map<WebviewHandle, WebviewEditorInput>();
private readonly _revivers = new Set<string>();
private _activeWebview: WebviewInput | undefined = undefined;
private _activeWebview: WebviewEditorInput | undefined = undefined;
constructor(
context: IExtHostContext,
@IContextKeyService _contextKeyService: IContextKeyService,
@IPartService private readonly _partService: IPartService,
@IContextKeyService contextKeyService: IContextKeyService,
@IEditorGroupService editorGroupService: IEditorGroupService,
@ILifecycleService lifecycleService: ILifecycleService,
@IWorkbenchEditorService private readonly _editorService: IWorkbenchEditorService,
@IEditorGroupService private readonly _editorGroupService: IEditorGroupService,
@IOpenerService private readonly _openerService: IOpenerService
@IWebviewService private readonly _webviewService: IWebviewService,
@IOpenerService private readonly _openerService: IOpenerService,
@IExtensionService private readonly _extensionService: IExtensionService,
) {
this._proxy = context.getProxy(ExtHostContext.ExtHostWebviews);
_editorGroupService.onEditorsChanged(this.onEditorsChanged, this, this._toDispose);
editorGroupService.onEditorsChanged(this.onEditorsChanged, this, this._toDispose);
_webviewService.registerReviver(MainThreadWebviews.viewType, this);
this._toDispose.push(lifecycleService.onWillShutdown(e => {
e.veto(this._onWillShutdown());
}));
}
dispose(): void {
@@ -51,30 +65,31 @@ export class MainThreadWebviews implements MainThreadWebviewsShape {
viewType: string,
title: string,
column: Position,
options: vscode.WebviewOptions,
options: WebviewInputOptions,
extensionFolderPath: string
): void {
const webviewInput = new WebviewInput(title, options, '', {
const webview = this._webviewService.createWebview(MainThreadWebviews.viewType, title, column, options, extensionFolderPath, {
onDidClickLink: uri => this.onDidClickLink(uri, webview.options),
onMessage: message => this._proxy.$onMessage(handle, message),
onDidChangePosition: position => this._proxy.$onDidChangePosition(handle, position),
onDispose: () => {
this._proxy.$onDidDisposeWeview(handle).then(() => {
this._webviews.delete(handle);
});
},
onDidClickLink: (link, options) => this.onDidClickLink(link, options)
}, this._partService);
}
});
this._webviews.set(handle, webviewInput);
webview.state = {
viewType: viewType,
state: undefined
};
this._editorService.openEditor(webviewInput, { pinned: true }, column);
this._webviews.set(handle, webview);
}
$disposeWebview(handle: WebviewHandle): void {
const webview = this.getWebview(handle);
if (webview) {
this._editorService.closeEditor(webview.position, webview);
}
webview.dispose();
}
$setTitle(handle: WebviewHandle, value: string): void {
@@ -84,24 +99,20 @@ export class MainThreadWebviews implements MainThreadWebviewsShape {
$setHtml(handle: WebviewHandle, value: string): void {
const webview = this.getWebview(handle);
webview.setHtml(value);
webview.html = value;
}
$reveal(handle: WebviewHandle, column: Position): void {
const webviewInput = this.getWebview(handle);
if (webviewInput.position === column) {
this._editorService.openEditor(webviewInput, { preserveFocus: true }, column);
} else {
this._editorGroupService.moveEditor(webviewInput, webviewInput.position, column, { preserveFocus: true });
}
const webview = this.getWebview(handle);
this._webviewService.revealWebview(webview, column);
}
async $sendMessage(handle: WebviewHandle, message: any): Promise<boolean> {
const webviewInput = this.getWebview(handle);
const webview = this.getWebview(handle);
const editors = this._editorService.getVisibleEditors()
.filter(e => e instanceof WebviewEditor)
.map(e => e as WebviewEditor)
.filter(e => e.input.matches(webviewInput));
.filter(e => e.input.matches(webview));
for (const editor of editors) {
editor.sendMessage(message);
@@ -110,18 +121,74 @@ export class MainThreadWebviews implements MainThreadWebviewsShape {
return (editors.length > 0);
}
private getWebview(handle: number): WebviewInput {
const webviewInput = this._webviews.get(handle);
if (!webviewInput) {
$registerSerializer(viewType: string): void {
this._revivers.add(viewType);
}
$unregisterSerializer(viewType: string): void {
this._revivers.delete(viewType);
}
reviveWebview(webview: WebviewEditorInput) {
this._extensionService.activateByEvent(`onView:${webview.state.viewType}`).then(() => {
const handle = 'revival-' + MainThreadWebviews.revivalPool++;
this._webviews.set(handle, webview);
webview._events = {
onDidClickLink: uri => this.onDidClickLink(uri, webview.options),
onMessage: message => this._proxy.$onMessage(handle, message),
onDidChangePosition: position => this._proxy.$onDidChangePosition(handle, position),
onDispose: () => {
this._proxy.$onDidDisposeWeview(handle).then(() => {
this._webviews.delete(handle);
});
}
};
this._proxy.$deserializeWebview(handle, webview.state.viewType, webview.state.state, webview.position, webview.options);
});
}
canRevive(webview: WebviewEditorInput): boolean {
return this._revivers.has(webview.viewType) || webview.reviver !== null;
}
private _onWillShutdown(): TPromise<boolean> {
const toRevive: WebviewHandle[] = [];
this._webviews.forEach((view, key) => {
if (this.canRevive(view)) {
toRevive.push(key);
}
});
const reviveResponses = toRevive.map(handle =>
this._proxy.$serializeWebview(handle).then(state => ({ handle, state })));
return TPromise.join(reviveResponses).then(results => {
for (const result of results) {
if (result.state) {
const view = this._webviews.get(result.handle);
if (view) {
view.state.state = result.state;
}
}
}
return false; // Don't veto shutdown
});
}
private getWebview(handle: WebviewHandle): WebviewEditorInput {
const webview = this._webviews.get(handle);
if (!webview) {
throw new Error('Unknown webview handle:' + handle);
}
return webviewInput;
return webview;
}
private onEditorsChanged() {
const activeEditor = this._editorService.getActiveEditor();
let newActiveWebview: { input: WebviewInput, handle: WebviewHandle } | undefined = undefined;
if (activeEditor && activeEditor.input instanceof WebviewInput) {
let newActiveWebview: { input: WebviewEditorInput, handle: WebviewHandle } | undefined = undefined;
if (activeEditor && activeEditor.input instanceof WebviewEditorInput) {
for (const handle of map.keys(this._webviews)) {
const input = this._webviews.get(handle);
if (input.matches(activeEditor.input)) {
@@ -132,7 +199,7 @@ export class MainThreadWebviews implements MainThreadWebviewsShape {
}
if (newActiveWebview) {
if (!this._activeWebview || !newActiveWebview.input.matches(this._activeWebview)) {
if (!this._activeWebview || newActiveWebview.input !== this._activeWebview) {
this._proxy.$onDidChangeActiveWeview(newActiveWebview.handle);
this._activeWebview = newActiveWebview.input;
}
@@ -144,7 +211,7 @@ export class MainThreadWebviews implements MainThreadWebviewsShape {
}
}
private onDidClickLink(link: URI, options: vscode.WebviewOptions): void {
private onDidClickLink(link: URI, options: WebviewInputOptions): void {
if (!link) {
return;
}
@@ -418,6 +418,9 @@ export function createApiFactory(
}),
createWebview: proposedApiFunction(extension, (viewType: string, title: string, column: vscode.ViewColumn, options: vscode.WebviewOptions) => {
return extHostWebviews.createWebview(viewType, title, column, options, extension.extensionFolderPath);
}),
registerWebviewSerializer: proposedApiFunction(extension, (viewType: string, serializer: vscode.WebviewSerializer) => {
return extHostWebviews.registerWebviewSerializer(viewType, serializer);
})
};
@@ -347,7 +347,7 @@ export interface MainThreadTelemetryShape extends IDisposable {
$publicLog(eventName: string, data?: any): void;
}
export type WebviewHandle = number;
export type WebviewHandle = string;
export interface MainThreadWebviewsShape extends IDisposable {
$createWebview(handle: WebviewHandle, viewType: string, title: string, column: EditorPosition, options: vscode.WebviewOptions, extensionFolderPath: string): void;
@@ -356,12 +356,18 @@ export interface MainThreadWebviewsShape extends IDisposable {
$setTitle(handle: WebviewHandle, value: string): void;
$setHtml(handle: WebviewHandle, value: string): void;
$sendMessage(handle: WebviewHandle, value: any): Thenable<boolean>;
$registerSerializer(viewType: string): void;
$unregisterSerializer(viewType: string): void;
}
export interface ExtHostWebviewsShape {
$onMessage(handle: WebviewHandle, message: any): void;
$onDidChangeActiveWeview(handle: WebviewHandle | undefined): void;
$onDidDisposeWeview(handle: WebviewHandle): Thenable<void>;
$onDidChangePosition(handle: WebviewHandle, newPosition: EditorPosition): void;
$deserializeWebview(newWebviewHandle: WebviewHandle, viewType: string, state: any, position: EditorPosition, options: vscode.WebviewOptions): void;
$serializeWebview(webviewHandle: WebviewHandle): Thenable<any>;
}
export interface MainThreadWorkspaceShape extends IDisposable {
@@ -137,6 +137,7 @@ export class ExtHostDocuments implements ExtHostDocumentsShape {
contentChanges: events.changes.map((change) => {
return {
range: TypeConverters.toRange(change.range),
rangeOffset: change.rangeOffset,
rangeLength: change.rangeLength,
text: change.text
};
@@ -152,7 +152,7 @@ class ExtHostTreeView<T> extends Disposable {
private resolveTreeNode(element: T, parent?: TreeNode): TPromise<TreeNode> {
return asWinJsPromise(() => this.dataProvider.getTreeItem(element))
.then(extTreeItem => this.createHandle(element, extTreeItem, parent))
.then(extTreeItem => this.createHandle(element, extTreeItem, parent, true))
.then(handle => this.getChildren(parent ? parent.item.handle : null)
.then(() => {
const cachedElement = this.getExtensionElement(handle);
@@ -303,7 +303,7 @@ class ExtHostTreeView<T> extends Disposable {
return item;
}
private createHandle(element: T, { id, label, resourceUri }: vscode.TreeItem, parent?: TreeNode): TreeItemHandle {
private createHandle(element: T, { id, label, resourceUri }: vscode.TreeItem, parent: TreeNode, first?: boolean): TreeItemHandle {
if (id) {
return `${ExtHostTreeView.ID_HANDLE_PREFIX}/${id}`;
}
@@ -316,7 +316,7 @@ class ExtHostTreeView<T> extends Disposable {
for (let counter = 0; counter <= childrenNodes.length; counter++) {
const handle = `${prefix}/${counter}:${elementId}`;
if (!this.elements.has(handle) || existingHandle === handle) {
if (first || !this.elements.has(handle) || existingHandle === handle) {
return handle;
}
}
+56 -4
View File
@@ -9,6 +9,7 @@ import { Event, Emitter } from 'vs/base/common/event';
import * as typeConverters from 'vs/workbench/api/node/extHostTypeConverters';
import { Position } from 'vs/platform/editor/common/editor';
import { TPromise } from 'vs/base/common/winjs.base';
import { Disposable } from './extHostTypes';
export class ExtHostWebview implements vscode.Webview {
@@ -19,6 +20,7 @@ export class ExtHostWebview implements vscode.Webview {
private _isDisposed: boolean = false;
private _viewColumn: vscode.ViewColumn;
private _active: boolean;
private _state: any;
public readonly onMessageEmitter = new Emitter<any>();
public readonly onDidReceiveMessage: Event<any> = this.onMessageEmitter.event;
@@ -85,6 +87,11 @@ export class ExtHostWebview implements vscode.Webview {
}
}
get state(): any {
this.assertNotDisposed();
return this._state;
}
get options(): vscode.WebviewOptions {
this.assertNotDisposed();
return this._options;
@@ -128,11 +135,12 @@ export class ExtHostWebview implements vscode.Webview {
}
export class ExtHostWebviews implements ExtHostWebviewsShape {
private static handlePool = 1;
private static webviewHandlePool = 1;
private readonly _proxy: MainThreadWebviewsShape;
private readonly _webviews = new Map<WebviewHandle, ExtHostWebview>();
private readonly _serializers = new Map<string, vscode.WebviewSerializer>();
private _activeWebview: ExtHostWebview | undefined;
@@ -149,7 +157,7 @@ export class ExtHostWebviews implements ExtHostWebviewsShape {
options: vscode.WebviewOptions,
extensionFolderPath: string
): vscode.Webview {
const handle = ExtHostWebviews.handlePool++;
const handle = ExtHostWebviews.webviewHandlePool++ + '';
this._proxy.$createWebview(handle, viewType, title, typeConverters.fromViewColumn(viewColumn), options, extensionFolderPath);
const webview = new ExtHostWebview(handle, this._proxy, viewType, viewColumn, options);
@@ -157,6 +165,23 @@ export class ExtHostWebviews implements ExtHostWebviewsShape {
return webview;
}
registerWebviewSerializer(
viewType: string,
serializer: vscode.WebviewSerializer
): vscode.Disposable {
if (this._serializers.has(viewType)) {
throw new Error(`Serializer for '${viewType}' already registered`);
}
this._serializers.set(viewType, serializer);
this._proxy.$registerSerializer(viewType);
return new Disposable(() => {
this._serializers.delete(viewType);
this._proxy.$unregisterSerializer(viewType);
});
}
$onMessage(handle: WebviewHandle, message: any): void {
const webview = this.getWebview(handle);
if (webview) {
@@ -206,8 +231,35 @@ export class ExtHostWebviews implements ExtHostWebviewsShape {
}
}
private readonly _onDidChangeActiveWebview = new Emitter<ExtHostWebview | undefined>();
public readonly onDidChangeActiveWebview = this._onDidChangeActiveWebview.event;
$deserializeWebview(
webviewHandle: WebviewHandle,
viewType: string,
state: any,
position: Position,
options: vscode.WebviewOptions
): void {
const serializer = this._serializers.get(viewType);
if (!serializer) {
return;
}
const revivedWebview = new ExtHostWebview(webviewHandle, this._proxy, viewType, typeConverters.toViewColumn(position), options);
this._webviews.set(webviewHandle, revivedWebview);
serializer.deserializeWebview(revivedWebview, state);
}
$serializeWebview(
webviewHandle: WebviewHandle
): Thenable<any> {
const webview = this.getWebview(webviewHandle);
const serialzer = this._serializers.get(webview.viewType);
if (!serialzer) {
return TPromise.as(undefined);
}
return serialzer.serializeWebview(webview);
}
private getWebview(handle: WebviewHandle) {
return this._webviews.get(handle);
+4 -5
View File
@@ -4,7 +4,6 @@
*--------------------------------------------------------------------------------------------*/
import { TPromise } from 'vs/base/common/winjs.base';
import { Builder } from 'vs/base/browser/builder';
import { IAction, IActionRunner, ActionRunner } from 'vs/base/common/actions';
import { IActionItem } from 'vs/base/browser/ui/actionbar/actionbar';
import { Component } from 'vs/workbench/common/component';
@@ -35,7 +34,7 @@ export abstract class Composite extends Component implements IComposite {
private _focusListenerDisposable?: IDisposable;
private visible: boolean;
private parent: Builder;
private parent: HTMLElement;
protected actionRunner: IActionRunner;
@@ -75,7 +74,7 @@ export abstract class Composite extends Component implements IComposite {
* Note that DOM-dependent calculations should be performed from the setVisible()
* call. Only then the composite will be part of the DOM.
*/
public create(parent: Builder): TPromise<void> {
public create(parent: HTMLElement): TPromise<void> {
this.parent = parent;
return TPromise.as(null);
@@ -88,12 +87,12 @@ export abstract class Composite extends Component implements IComposite {
/**
* Returns the container this composite is being build in.
*/
public getContainer(): Builder {
public getContainer(): HTMLElement {
return this.parent;
}
public get onDidFocus(): Event<any> {
this._focusTracker = trackFocus(this.getContainer().getHTMLElement());
this._focusTracker = trackFocus(this.getContainer());
this._focusListenerDisposable = this._focusTracker.onDidFocus(() => {
this._onDidFocus.fire();
});
+34 -34
View File
@@ -22,7 +22,7 @@ import { IThemeService } from 'vs/platform/theme/common/themeService';
import { memoize } from 'vs/base/common/decorators';
import { NotificationsCenter } from 'vs/workbench/browser/parts/notifications/notificationsCenter';
import { NotificationsToasts } from 'vs/workbench/browser/parts/notifications/notificationsToasts';
import { Dimension, getClientArea } from 'vs/base/browser/dom';
import { Dimension, getClientArea, size, position, hide, show } from 'vs/base/browser/dom';
const MIN_SIDEBAR_PART_WIDTH = 170;
const DEFAULT_SIDEBAR_PART_WIDTH = 300;
@@ -578,14 +578,8 @@ export class WorkbenchLayout implements IVerticalSashLayoutProvider, IHorizontal
}
// Workbench
this.workbenchContainer.style.top = '0px';
this.workbenchContainer.style.right = '0px';
this.workbenchContainer.style.bottom = '0px';
this.workbenchContainer.style.left = '0px';
this.workbenchContainer.style.position = 'relative';
this.workbenchContainer.style.width = `${this.workbenchSize.width}px`;
this.workbenchContainer.style.height = `${this.workbenchSize.height}px`;
position(this.workbenchContainer, 0, 0, 0, 0, 'relative');
size(this.workbenchContainer, this.workbenchSize.width, this.workbenchSize.height);
// Bug on Chrome: Sometimes Chrome wants to scroll the workbench container on layout changes. The fix is to reset scrolling in this case.
const workbenchContainer = this.workbenchContainer;
@@ -597,64 +591,70 @@ export class WorkbenchLayout implements IVerticalSashLayoutProvider, IHorizontal
}
// Title Part
const titleContainer = this.titlebar.getContainer();
if (isTitlebarHidden) {
this.titlebar.getContainer().hide();
hide(titleContainer);
} else {
this.titlebar.getContainer().show();
show(titleContainer);
}
// Editor Part and Panel part
this.editor.getContainer().size(editorSize.width, editorSize.height);
this.panel.getContainer().size(panelDimension.width, panelDimension.height);
const editorContainer = this.editor.getContainer();
const panelContainer = this.panel.getContainer();
size(editorContainer, editorSize.width, editorSize.height);
size(panelContainer, panelDimension.width, panelDimension.height);
if (panelPosition === Position.BOTTOM) {
if (sidebarPosition === Position.LEFT) {
this.editor.getContainer().position(this.titlebarHeight, 0, this.statusbarHeight + panelDimension.height, sidebarSize.width + activityBarSize.width);
this.panel.getContainer().position(editorSize.height + this.titlebarHeight, 0, this.statusbarHeight, sidebarSize.width + activityBarSize.width);
position(editorContainer, this.titlebarHeight, 0, this.statusbarHeight + panelDimension.height, sidebarSize.width + activityBarSize.width);
position(panelContainer, editorSize.height + this.titlebarHeight, 0, this.statusbarHeight, sidebarSize.width + activityBarSize.width);
} else {
this.editor.getContainer().position(this.titlebarHeight, sidebarSize.width, this.statusbarHeight + panelDimension.height, 0);
this.panel.getContainer().position(editorSize.height + this.titlebarHeight, sidebarSize.width, this.statusbarHeight, 0);
position(editorContainer, this.titlebarHeight, sidebarSize.width, this.statusbarHeight + panelDimension.height, 0);
position(panelContainer, editorSize.height + this.titlebarHeight, sidebarSize.width, this.statusbarHeight, 0);
}
} else {
if (sidebarPosition === Position.LEFT) {
this.editor.getContainer().position(this.titlebarHeight, panelDimension.width, this.statusbarHeight, sidebarSize.width + activityBarSize.width);
this.panel.getContainer().position(this.titlebarHeight, 0, this.statusbarHeight, sidebarSize.width + activityBarSize.width + editorSize.width);
position(editorContainer, this.titlebarHeight, panelDimension.width, this.statusbarHeight, sidebarSize.width + activityBarSize.width);
position(panelContainer, this.titlebarHeight, 0, this.statusbarHeight, sidebarSize.width + activityBarSize.width + editorSize.width);
} else {
this.editor.getContainer().position(this.titlebarHeight, sidebarSize.width + activityBarSize.width + panelWidth, this.statusbarHeight, 0);
this.panel.getContainer().position(this.titlebarHeight, sidebarSize.width + activityBarSize.width, this.statusbarHeight, editorSize.width);
position(editorContainer, this.titlebarHeight, sidebarSize.width + activityBarSize.width + panelWidth, this.statusbarHeight, 0);
position(panelContainer, this.titlebarHeight, sidebarSize.width + activityBarSize.width, this.statusbarHeight, editorSize.width);
}
}
// Activity Bar Part
this.activitybar.getContainer().size(null, activityBarSize.height);
const activitybarContainer = this.activitybar.getContainer();
size(activitybarContainer, null, activityBarSize.height);
if (sidebarPosition === Position.LEFT) {
this.activitybar.getContainer().getHTMLElement().style.right = '';
this.activitybar.getContainer().position(this.titlebarHeight, null, 0, 0);
this.activitybar.getContainer().style.right = '';
position(activitybarContainer, this.titlebarHeight, null, 0, 0);
} else {
this.activitybar.getContainer().getHTMLElement().style.left = '';
this.activitybar.getContainer().position(this.titlebarHeight, 0, 0, null);
this.activitybar.getContainer().style.left = '';
position(activitybarContainer, this.titlebarHeight, 0, 0, null);
}
if (isActivityBarHidden) {
this.activitybar.getContainer().hide();
hide(activitybarContainer);
} else {
this.activitybar.getContainer().show();
show(activitybarContainer);
}
// Sidebar Part
this.sidebar.getContainer().size(sidebarSize.width, sidebarSize.height);
const sidebarContainer = this.sidebar.getContainer();
size(sidebarContainer, sidebarSize.width, sidebarSize.height);
const editorAndPanelWidth = editorSize.width + (panelPosition === Position.RIGHT ? panelWidth : 0);
if (sidebarPosition === Position.LEFT) {
this.sidebar.getContainer().position(this.titlebarHeight, editorAndPanelWidth, this.statusbarHeight, activityBarSize.width);
position(sidebarContainer, this.titlebarHeight, editorAndPanelWidth, this.statusbarHeight, activityBarSize.width);
} else {
this.sidebar.getContainer().position(this.titlebarHeight, activityBarSize.width, this.statusbarHeight, editorAndPanelWidth);
position(sidebarContainer, this.titlebarHeight, activityBarSize.width, this.statusbarHeight, editorAndPanelWidth);
}
// Statusbar Part
this.statusbar.getContainer().position(this.workbenchSize.height - this.statusbarHeight);
const statusbarContainer = this.statusbar.getContainer();
position(statusbarContainer, this.workbenchSize.height - this.statusbarHeight);
if (isStatusbarHidden) {
this.statusbar.getContainer().hide();
hide(statusbarContainer);
} else {
this.statusbar.getContainer().show();
show(statusbarContainer);
}
// Quick open
+1 -1
View File
@@ -95,7 +95,7 @@ export abstract class TogglePanelAction extends Action {
const activePanel = this.panelService.getActivePanel();
const activeElement = document.activeElement;
return activePanel && activeElement && DOM.isAncestor(activeElement, (<Panel>activePanel).getContainer().getHTMLElement());
return activePanel && activeElement && DOM.isAncestor(activeElement, (<Panel>activePanel).getContainer());
}
}
+12 -13
View File
@@ -6,10 +6,9 @@
'use strict';
import 'vs/css!./media/part';
import { Builder } from 'vs/base/browser/builder';
import { Component } from 'vs/workbench/common/component';
import { IThemeService, ITheme } from 'vs/platform/theme/common/themeService';
import { Dimension } from 'vs/base/browser/dom';
import { Dimension, size } from 'vs/base/browser/dom';
export interface IPartOptions {
hasTitle?: boolean;
@@ -21,9 +20,9 @@ export interface IPartOptions {
* and mandatory content area to show content.
*/
export abstract class Part extends Component {
private parent: Builder;
private titleArea: Builder;
private contentArea: Builder;
private parent: HTMLElement;
private titleArea: HTMLElement;
private contentArea: HTMLElement;
private partLayout: PartLayout;
constructor(
@@ -48,7 +47,7 @@ export abstract class Part extends Component {
*
* Called to create title and content area of the part.
*/
public create(parent: Builder): void {
public create(parent: HTMLElement): void {
this.parent = parent;
this.titleArea = this.createTitleArea(parent);
this.contentArea = this.createContentArea(parent);
@@ -61,35 +60,35 @@ export abstract class Part extends Component {
/**
* Returns the overall part container.
*/
public getContainer(): Builder {
public getContainer(): HTMLElement {
return this.parent;
}
/**
* Subclasses override to provide a title area implementation.
*/
protected createTitleArea(parent: Builder): Builder {
protected createTitleArea(parent: HTMLElement): HTMLElement {
return null;
}
/**
* Returns the title area container.
*/
protected getTitleArea(): Builder {
protected getTitleArea(): HTMLElement {
return this.titleArea;
}
/**
* Subclasses override to provide a content area implementation.
*/
protected createContentArea(parent: Builder): Builder {
protected createContentArea(parent: HTMLElement): HTMLElement {
return null;
}
/**
* Returns the content area container.
*/
protected getContentArea(): Builder {
protected getContentArea(): HTMLElement {
return this.contentArea;
}
@@ -105,7 +104,7 @@ const TITLE_HEIGHT = 35;
export class PartLayout {
constructor(container: Builder, private options: IPartOptions, titleArea: Builder, private contentArea: Builder) { }
constructor(container: HTMLElement, private options: IPartOptions, titleArea: HTMLElement, private contentArea: HTMLElement) { }
public layout(dimension: Dimension): Dimension[] {
const { width, height } = dimension;
@@ -133,7 +132,7 @@ export class PartLayout {
// Content
if (this.contentArea) {
this.contentArea.size(contentSize.width, contentSize.height);
size(this.contentArea, contentSize.width, contentSize.height);
}
return sizes;
@@ -9,7 +9,7 @@ import 'vs/css!./media/activitybarpart';
import * as nls from 'vs/nls';
import { TPromise } from 'vs/base/common/winjs.base';
import { illegalArgument } from 'vs/base/common/errors';
import { Builder, $ } from 'vs/base/browser/builder';
import { $ } from 'vs/base/browser/builder';
import { Action } from 'vs/base/common/actions';
import { ActionsOrientation, ActionBar, Separator } from 'vs/base/browser/ui/actionbar/actionbar';
import { GlobalActivityExtensions, IGlobalActivityRegistry } from 'vs/workbench/common/activity';
@@ -125,7 +125,7 @@ export class ActivitybarPart extends Part {
return toDisposable(() => action.setBadge(undefined));
}
public createContentArea(parent: Builder): Builder {
public createContentArea(parent: HTMLElement): HTMLElement {
const $el = $(parent);
const $result = $('.content').appendTo($el);
@@ -156,14 +156,14 @@ export class ActivitybarPart extends Part {
});
}
return $result;
return $result.getHTMLElement();
}
public updateStyles(): void {
super.updateStyles();
// Part container
const container = this.getContainer();
const container = $(this.getContainer());
const background = this.getColor(ACTIVITY_BAR_BACKGROUND);
container.style('background-color', background);
@@ -224,7 +224,7 @@ export abstract class CompositePart<T extends Composite> extends Part {
'class': ['composite', this.compositeCSSClass],
id: composite.getId()
}, div => {
createCompositePromise = composite.create(div).then(() => {
createCompositePromise = composite.create(div.getHTMLElement()).then(() => {
composite.updateStyles();
});
});
@@ -401,7 +401,7 @@ export abstract class CompositePart<T extends Composite> extends Part {
});
}
public createTitleArea(parent: Builder): Builder {
public createTitleArea(parent: HTMLElement): HTMLElement {
// Title Area Container
const titleArea = $(parent).div({
@@ -411,7 +411,7 @@ export abstract class CompositePart<T extends Composite> extends Part {
$(titleArea).on(EventType.CONTEXT_MENU, (e: MouseEvent) => this.onTitleAreaContextMenu(new StandardMouseEvent(e)));
// Left Title Label
this.titleLabel = this.createTitleLabel(titleArea);
this.titleLabel = this.createTitleLabel(titleArea.getHTMLElement());
// Right Actions Container
$(titleArea).div({
@@ -426,10 +426,10 @@ export abstract class CompositePart<T extends Composite> extends Part {
});
});
return titleArea;
return titleArea.getHTMLElement();
}
protected createTitleLabel(parent: Builder): ICompositeTitleLabel {
protected createTitleLabel(parent: HTMLElement): ICompositeTitleLabel {
let titleLabel: Builder;
$(parent).div({
'class': 'title-label'
@@ -486,14 +486,14 @@ export abstract class CompositePart<T extends Composite> extends Part {
return undefined;
}
public createContentArea(parent: Builder): Builder {
public createContentArea(parent: HTMLElement): HTMLElement {
return $(parent).div({
'class': 'content'
}, div => {
this.progressBar = new ProgressBar(div.getHTMLElement());
this.toUnbind.push(attachProgressBarStyler(this.progressBar, this.themeService));
this.progressBar.hide();
});
}).getHTMLElement();
}
private onError(error: any): void {
@@ -5,7 +5,6 @@
'use strict';
import { TPromise } from 'vs/base/common/winjs.base';
import { Builder } from 'vs/base/browser/builder';
import { Panel } from 'vs/workbench/browser/panel';
import { EditorInput, EditorOptions } from 'vs/workbench/common/editor';
import { IEditor, Position } from 'vs/platform/editor/common/editor';
@@ -64,9 +63,9 @@ export abstract class BaseEditor extends Panel implements IEditor {
this._options = null;
}
public create(parent: Builder): void; // create is sync for editors
public create(parent: Builder): TPromise<void>;
public create(parent: Builder): TPromise<void> {
public create(parent: HTMLElement): void; // create is sync for editors
public create(parent: HTMLElement): TPromise<void>;
public create(parent: HTMLElement): TPromise<void> {
const res = super.create(parent);
// Create Editor
@@ -76,9 +75,9 @@ export abstract class BaseEditor extends Panel implements IEditor {
}
/**
* Called to create the editor in the parent builder.
* Called to create the editor in the parent HTMLElement.
*/
protected abstract createEditor(parent: Builder): void;
protected abstract createEditor(parent: HTMLElement): void;
/**
* Overload this function to allow for passing in a position argument.
@@ -60,7 +60,7 @@ export abstract class BaseBinaryResourceEditor extends BaseEditor {
return this.input ? this.input.getName() : nls.localize('binaryEditor', "Binary Viewer");
}
protected createEditor(parent: Builder): void {
protected createEditor(parent: HTMLElement): void {
// Container for Binary
const binaryContainerElement = document.createElement('div');
@@ -71,7 +71,7 @@ export abstract class BaseBinaryResourceEditor extends BaseEditor {
// Custom Scrollbars
this.scrollbar = new DomScrollableElement(binaryContainerElement, { horizontal: ScrollbarVisibility.Auto, vertical: ScrollbarVisibility.Auto });
parent.getHTMLElement().appendChild(this.scrollbar.getDomNode());
parent.appendChild(this.scrollbar.getDomNode());
}
public setInput(input: EditorInput, options?: EditorOptions): TPromise<void> {
@@ -99,7 +99,7 @@ export abstract class BaseBinaryResourceEditor extends BaseEditor {
// Render Input
this.resourceViewerContext = ResourceViewer.show(
{ name: model.getName(), resource: model.getResource(), size: model.getSize(), etag: model.getETag(), mime: model.getMime() },
this.binaryContainer,
this.binaryContainer.getHTMLElement(),
this.scrollbar,
resource => this.callbacks.openInternal(input, options),
resource => this.callbacks.openExternal(resource),
@@ -118,7 +118,7 @@ export class EditorGroupsControl extends Themable implements IEditorGroupsContro
private stacks: IEditorStacksModel;
private parent: Builder;
private parent: HTMLElement;
private dimension: DOM.Dimension;
private dragging: boolean;
@@ -168,7 +168,7 @@ export class EditorGroupsControl extends Themable implements IEditorGroupsContro
private transfer = LocalSelectionTransfer.getInstance<DraggedEditorIdentifier>();
constructor(
parent: Builder,
parent: HTMLElement,
groupOrientation: GroupOrientation,
@IWorkbenchEditorService private editorService: IWorkbenchEditorService,
@IEditorGroupService private editorGroupService: IEditorGroupService,
@@ -367,8 +367,8 @@ export class EditorGroupsControl extends Themable implements IEditorGroupsContro
this.trackFocus(editor, position);
// Find target container and build into
const target = this.silos[position].child();
editor.getContainer().build(target);
const target = this.silos[position].child().getHTMLElement();
target.appendChild(editor.getContainer());
// Adjust layout according to provided ratios (used when restoring multiple editors at once)
if (ratio && (ratio.length === 2 || ratio.length === 3)) {
@@ -440,7 +440,7 @@ export class EditorGroupsControl extends Themable implements IEditorGroupsContro
}
// Show editor container
editor.getContainer().show();
DOM.show(editor.getContainer());
}
private getVisibleEditorCount(): number {
@@ -552,7 +552,11 @@ export class EditorGroupsControl extends Themable implements IEditorGroupsContro
this.clearPosition(position);
// Take editor container offdom and hide
editor.getContainer().offDOM().hide();
const editorContainer = editor.getContainer();
if (editorContainer.parentNode) {
editorContainer.parentNode.removeChild(editorContainer);
}
DOM.hide(editorContainer);
// Adjust layout and rochade if instructed to do so
if (layoutAndRochade) {
@@ -778,10 +782,10 @@ export class EditorGroupsControl extends Themable implements IEditorGroupsContro
this.layoutVertically = (orientation !== 'horizontal');
// Editor Layout
const verticalLayouting = this.parent.hasClass('vertical-layout');
const verticalLayouting = DOM.hasClass(this.parent, 'vertical-layout');
if (verticalLayouting !== this.layoutVertically) {
this.parent.removeClass('vertical-layout', 'horizontal-layout');
this.parent.addClass(this.layoutVertically ? 'vertical-layout' : 'horizontal-layout');
DOM.removeClasses(this.parent, 'vertical-layout', 'horizontal-layout');
DOM.addClass(this.parent, this.layoutVertically ? 'vertical-layout' : 'horizontal-layout');
this.sashOne.setOrientation(this.layoutVertically ? Orientation.VERTICAL : Orientation.HORIZONTAL);
this.sashTwo.setOrientation(this.layoutVertically ? Orientation.VERTICAL : Orientation.HORIZONTAL);
@@ -966,16 +970,16 @@ export class EditorGroupsControl extends Themable implements IEditorGroupsContro
private create(): void {
// Store layout as class property
this.parent.addClass(this.layoutVertically ? 'vertical-layout' : 'horizontal-layout');
DOM.addClass(this.parent, this.layoutVertically ? 'vertical-layout' : 'horizontal-layout');
// Allow to drop into container to open
this.enableDropTarget(this.parent.getHTMLElement());
this.enableDropTarget(this.parent);
// Silo One
this.silos[Position.ONE] = $(this.parent).div({ class: 'one-editor-silo editor-one' });
// Sash One
this.sashOne = new Sash(this.parent.getHTMLElement(), this, { baseSize: 5, orientation: this.layoutVertically ? Orientation.VERTICAL : Orientation.HORIZONTAL });
this.sashOne = new Sash(this.parent, this, { baseSize: 5, orientation: this.layoutVertically ? Orientation.VERTICAL : Orientation.HORIZONTAL });
this.toUnbind.push(this.sashOne.onDidStart(() => this.onSashOneDragStart()));
this.toUnbind.push(this.sashOne.onDidChange((e: ISashEvent) => this.onSashOneDrag(e)));
this.toUnbind.push(this.sashOne.onDidEnd(() => this.onSashOneDragEnd()));
@@ -986,7 +990,7 @@ export class EditorGroupsControl extends Themable implements IEditorGroupsContro
this.silos[Position.TWO] = $(this.parent).div({ class: 'one-editor-silo editor-two' });
// Sash Two
this.sashTwo = new Sash(this.parent.getHTMLElement(), this, { baseSize: 5, orientation: this.layoutVertically ? Orientation.VERTICAL : Orientation.HORIZONTAL });
this.sashTwo = new Sash(this.parent, this, { baseSize: 5, orientation: this.layoutVertically ? Orientation.VERTICAL : Orientation.HORIZONTAL });
this.toUnbind.push(this.sashTwo.onDidStart(() => this.onSashTwoDragStart()));
this.toUnbind.push(this.sashTwo.onDidChange((e: ISashEvent) => this.onSashTwoDrag(e)));
this.toUnbind.push(this.sashTwo.onDidEnd(() => this.onSashTwoDragEnd()));
@@ -1282,7 +1286,7 @@ export class EditorGroupsControl extends Themable implements IEditorGroupsContro
if (!overlay) {
const containers = $this.visibleEditors.filter(e => !!e).map(e => e.getContainer());
containers.forEach((container, index) => {
if (container && DOM.isAncestor(target, container.getHTMLElement())) {
if (container && DOM.isAncestor(target, container)) {
const activeContrastBorderColor = $this.getColor(activeContrastBorder);
overlay = $('div').style({
top: $this.tabOptions.showTabs ? `${EditorGroupsControl.EDITOR_TITLE_HEIGHT}px` : 0,
@@ -1625,11 +1629,11 @@ export class EditorGroupsControl extends Themable implements IEditorGroupsContro
let borderColor = null;
if (isDragging) {
this.parent.addClass('dragging');
DOM.addClass(this.parent, 'dragging');
silo.addClass('dragging');
borderColor = this.getColor(EDITOR_GROUP_BORDER) || this.getColor(contrastBorder);
} else {
this.parent.removeClass('dragging');
DOM.removeClass(this.parent, 'dragging');
silo.removeClass('dragging');
}
@@ -2201,9 +2205,9 @@ export class EditorGroupsControl extends Themable implements IEditorGroupsContro
}
const editorContainer = editor.getContainer();
editorContainer.style('margin-left', this.centeredEditorActive ? `${editorPosition}px` : null);
editorContainer.style('width', this.centeredEditorActive ? `${editorWidth}px` : null);
editorContainer.style('border-color', this.centeredEditorActive ? this.getColor(EDITOR_GROUP_BORDER) || this.getColor(contrastBorder) : null);
editorContainer.style.marginLeft = this.centeredEditorActive ? `${editorPosition}px` : null;
editorContainer.style.width = this.centeredEditorActive ? `${editorWidth}px` : null;
editorContainer.style.borderColor = this.centeredEditorActive ? this.getColor(EDITOR_GROUP_BORDER) || this.getColor(contrastBorder) : null;
editor.layout(new DOM.Dimension(editorWidth, editorHeight));
}
}
@@ -9,7 +9,6 @@ import 'vs/css!./media/editorpart';
import 'vs/workbench/browser/parts/editor/editor.contribution';
import { TPromise } from 'vs/base/common/winjs.base';
import { Registry } from 'vs/platform/registry/common/platform';
import { Builder, $ } from 'vs/base/browser/builder';
import * as nls from 'vs/nls';
import * as strings from 'vs/base/common/strings';
import * as arrays from 'vs/base/common/arrays';
@@ -40,7 +39,7 @@ import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/c
import { IThemeService } from 'vs/platform/theme/common/themeService';
import { editorBackground } from 'vs/platform/theme/common/colorRegistry';
import { EDITOR_GROUP_BACKGROUND } from 'vs/workbench/common/theme';
import { createCSSRule, Dimension } from 'vs/base/browser/dom';
import { createCSSRule, Dimension, addClass, removeClass } from 'vs/base/browser/dom';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { join } from 'vs/base/common/paths';
import { IEditorDescriptor, IEditorRegistry, Extensions as EditorExtensions } from 'vs/workbench/browser/editor';
@@ -470,11 +469,12 @@ export class EditorPart extends Part implements IEditorPart, IEditorGroupService
// Create editor as needed
if (!editor.getContainer()) {
editor.create($().div({
'class': 'editor-container',
'role': 'tabpanel',
id: descriptor.getId()
}));
const editorContainer = document.createElement('div');
editorContainer.id = descriptor.getId();
addClass(editorContainer, 'editor-container');
editorContainer.setAttribute('role', 'tabpanel');
editor.create(editorContainer);
}
return editor;
@@ -559,7 +559,7 @@ export class EditorPart extends Part implements IEditorPart, IEditorGroupService
actions
});
once(handle.onDidDispose)(() => dispose(actions.primary));
once(handle.onDidClose)(() => dispose(actions.primary));
}
this.editorGroupsControl.updateProgress(position, ProgressState.DONE);
@@ -1134,12 +1134,12 @@ export class EditorPart extends Part implements IEditorPart, IEditorGroupService
return this.editorGroupsControl.getGroupOrientation();
}
public createContentArea(parent: Builder): Builder {
public createContentArea(parent: HTMLElement): HTMLElement {
// Content Container
const contentArea = $(parent)
.div()
.addClass('content');
const contentArea = document.createElement('div');
addClass(contentArea, 'content');
parent.appendChild(contentArea);
// get settings
this.memento = this.getMemento(this.storageService, MementoScope.WORKSPACE);
@@ -1157,19 +1157,19 @@ export class EditorPart extends Part implements IEditorPart, IEditorGroupService
// Part container
const container = this.getContainer();
container.style('background-color', this.getColor(editorBackground));
container.style.backgroundColor = this.getColor(editorBackground);
// Content area
const content = this.getContentArea();
const groupCount = this.stacks.groups.length;
if (groupCount > 1) {
content.addClass('multiple-groups');
addClass(content, 'multiple-groups');
} else {
content.removeClass('multiple-groups');
removeClass(content, 'multiple-groups');
}
content.style('background-color', groupCount > 0 ? this.getColor(EDITOR_GROUP_BACKGROUND) : null);
content.style.backgroundColor = groupCount > 0 ? this.getColor(EDITOR_GROUP_BACKGROUND) : null;
}
private onGroupFocusChanged(): void {
@@ -132,7 +132,7 @@ export class ResourceViewer {
public static show(
descriptor: IResourceDescriptor,
container: Builder,
container: HTMLElement,
scrollbar: DomScrollableElement,
openInternalClb: (uri: URI) => void,
openExternalClb: (uri: URI) => void,
@@ -184,7 +184,7 @@ class ImageView {
private static readonly BASE64_MARKER = 'base64,';
public static create(
container: Builder,
container: HTMLElement,
descriptor: IResourceDescriptor,
scrollbar: DomScrollableElement,
openExternalClb: (uri: URI) => void,
@@ -221,7 +221,7 @@ class ImageView {
class LargeImageView {
public static create(
container: Builder,
container: HTMLElement,
descriptor: IResourceDescriptor,
openExternalClb: (uri: URI) => void
) {
@@ -247,7 +247,7 @@ class LargeImageView {
class FileTooLargeFileView {
public static create(
container: Builder,
container: HTMLElement,
descriptor: IResourceDescriptor,
scrollbar: DomScrollableElement,
metadataClb: (meta: string) => void
@@ -270,7 +270,7 @@ class FileTooLargeFileView {
class FileSeemsBinaryFileView {
public static create(
container: Builder,
container: HTMLElement,
descriptor: IResourceDescriptor,
scrollbar: DomScrollableElement,
openInternalClb: (uri: URI) => void,
@@ -434,7 +434,7 @@ class InlineImageView {
private static readonly imageStateCache = new LRUCache<string, ImageState>(100);
public static create(
container: Builder,
container: HTMLElement,
descriptor: IResourceDescriptor,
scrollbar: DomScrollableElement,
metadataClb: (meta: string) => void
@@ -5,7 +5,6 @@
import { TPromise } from 'vs/base/common/winjs.base';
import * as DOM from 'vs/base/browser/dom';
import { Builder } from 'vs/base/browser/builder';
import { Registry } from 'vs/platform/registry/common/platform';
import { EditorInput, EditorOptions, SideBySideEditorInput } from 'vs/workbench/common/editor';
import { BaseEditor } from 'vs/workbench/browser/parts/editor/baseEditor';
@@ -39,10 +38,9 @@ export class SideBySideEditor extends BaseEditor {
super(SideBySideEditor.ID, telemetryService, themeService);
}
protected createEditor(parent: Builder): void {
const parentElement = parent.getHTMLElement();
DOM.addClass(parentElement, 'side-by-side-editor');
this.createSash(parentElement);
protected createEditor(parent: HTMLElement): void {
DOM.addClass(parent, 'side-by-side-editor');
this.createSash(parent);
}
public setInput(newInput: SideBySideEditorInput, options?: EditorOptions): TPromise<void> {
@@ -135,7 +133,7 @@ export class SideBySideEditor extends BaseEditor {
const descriptor = Registry.as<IEditorRegistry>(EditorExtensions.Editors).getEditor(editorInput);
const editor = descriptor.instantiate(this.instantiationService);
editor.create(new Builder(container));
editor.create(container);
editor.setVisible(this.isVisible(), this.position);
return editor;
@@ -149,7 +147,7 @@ export class SideBySideEditor extends BaseEditor {
}
private createEditorContainers(): void {
const parentElement = this.getContainer().getHTMLElement();
const parentElement = this.getContainer();
this.detailsEditorContainer = DOM.append(parentElement, DOM.$('.details-editor-container'));
this.detailsEditorContainer.style.position = 'absolute';
this.masterEditorContainer = DOM.append(parentElement, DOM.$('.master-editor-container'));
@@ -191,7 +189,7 @@ export class SideBySideEditor extends BaseEditor {
}
private disposeEditors(): void {
const parentContainer = this.getContainer().getHTMLElement();
const parentContainer = this.getContainer();
if (this.detailsEditor) {
this.detailsEditor.dispose();
this.detailsEditor = null;
@@ -9,7 +9,6 @@ import 'vs/css!./media/textdiffeditor';
import { TPromise } from 'vs/base/common/winjs.base';
import * as nls from 'vs/nls';
import * as objects from 'vs/base/common/objects';
import { Builder } from 'vs/base/browser/builder';
import { Action, IAction } from 'vs/base/common/actions';
import { onUnexpectedError } from 'vs/base/common/errors';
import * as types from 'vs/base/common/types';
@@ -82,7 +81,7 @@ export class TextDiffEditor extends BaseTextEditor {
return nls.localize('textDiffEditor', "Text Diff Editor");
}
public createEditorControl(parent: Builder, configuration: IEditorOptions): IDiffEditor {
public createEditorControl(parent: HTMLElement, configuration: IEditorOptions): IDiffEditor {
// Actions
this.nextDiffAction = new NavigateAction(this, true);
@@ -122,7 +121,7 @@ export class TextDiffEditor extends BaseTextEditor {
// Create a special child of instantiator that will delegate all calls to openEditor() to the same diff editor if the input matches with the modified one
const diffEditorInstantiator = this.instantiationService.createChild(new ServiceCollection([IWorkbenchEditorService, delegatingEditorService]));
return diffEditorInstantiator.createInstance(DiffEditorWidget, parent.getHTMLElement(), configuration);
return diffEditorInstantiator.createInstance(DiffEditorWidget, parent, configuration);
}
public setInput(input: EditorInput, options?: EditorOptions): TPromise<void> {
@@ -8,7 +8,6 @@
import * as nls from 'vs/nls';
import { TPromise } from 'vs/base/common/winjs.base';
import URI from 'vs/base/common/uri';
import { Builder } from 'vs/base/browser/builder';
import * as objects from 'vs/base/common/objects';
import * as types from 'vs/base/common/types';
import * as errors from 'vs/base/common/errors';
@@ -43,7 +42,7 @@ export interface IEditorConfiguration {
*/
export abstract class BaseTextEditor extends BaseEditor {
private editorControl: IEditor;
private _editorContainer: Builder;
private _editorContainer: HTMLElement;
private hasPendingConfigurationChange: boolean;
private lastAppliedEditorOptions: IEditorOptions;
@@ -123,7 +122,7 @@ export abstract class BaseTextEditor extends BaseEditor {
return overrides;
}
protected createEditor(parent: Builder): void {
protected createEditor(parent: HTMLElement): void {
// Editor for Text
this._editorContainer = parent;
@@ -177,10 +176,10 @@ export abstract class BaseTextEditor extends BaseEditor {
*
* The passed in configuration object should be passed to the editor control when creating it.
*/
protected createEditorControl(parent: Builder, configuration: IEditorOptions): IEditor {
protected createEditorControl(parent: HTMLElement, configuration: IEditorOptions): IEditor {
// Use a getter for the instantiation service since some subclasses might use scoped instantiation services
return this.instantiationService.createInstance(CodeEditor, parent.getHTMLElement(), configuration);
return this.instantiationService.createInstance(CodeEditor, parent, configuration);
}
public setInput(input: EditorInput, options?: EditorOptions): TPromise<void> {
@@ -189,7 +188,7 @@ export abstract class BaseTextEditor extends BaseEditor {
// Update editor options after having set the input. We do this because there can be
// editor input specific options (e.g. an ARIA label depending on the input showing)
this.updateEditorConfiguration();
this._editorContainer.getHTMLElement().setAttribute('aria-label', this.computeAriaLabel());
this._editorContainer.setAttribute('aria-label', this.computeAriaLabel());
});
}
@@ -289,9 +289,9 @@ export class NotificationsCenter extends Themable {
// Hide notifications center first
this.hide();
// Dispose all
// Close all
while (this.model.notifications.length) {
this.model.notifications[0].dispose();
this.model.notifications[0].close();
}
}
}
@@ -115,7 +115,7 @@ export function registerNotificationCommands(center: INotificationsCenterControl
handler: (accessor, args?: any) => {
const notification = getNotificationFromContext(accessor.get(IListService), args);
if (notification) {
notification.dispose();
notification.close();
}
}
});
@@ -168,8 +168,8 @@ export class NotificationsToasts extends Themable {
}
}));
// Remove when item gets disposed
once(item.onDidDispose)(() => {
// Remove when item gets closed
once(item.onDidClose)(() => {
this.removeToast(item);
});
@@ -442,7 +442,7 @@ export class NotificationTemplateRenderer {
this.actionRunner.run(action, notification);
// Hide notification
notification.dispose();
notification.close();
}));
this.inputDisposeables.push(attachButtonStyler(button, this.themeService));
@@ -7,7 +7,7 @@ import 'vs/css!./media/panelpart';
import { TPromise } from 'vs/base/common/winjs.base';
import { IAction, Action } from 'vs/base/common/actions';
import { Event } from 'vs/base/common/event';
import { Builder } from 'vs/base/browser/builder';
import { $ } from 'vs/base/browser/builder';
import { Registry } from 'vs/platform/registry/common/platform';
import { ActionsOrientation } from 'vs/base/browser/ui/actionbar/actionbar';
import { IPanel } from 'vs/workbench/common/panel';
@@ -124,11 +124,11 @@ export class PanelPart extends CompositePart<Panel> implements IPanelService {
public updateStyles(): void {
super.updateStyles();
const container = this.getContainer();
const container = $(this.getContainer());
container.style('background-color', this.getColor(PANEL_BACKGROUND));
container.style('border-left-color', this.getColor(PANEL_BORDER) || this.getColor(contrastBorder));
const title = this.getTitleArea();
const title = $(this.getTitleArea());
title.style('border-top-color', this.getColor(PANEL_BORDER) || this.getColor(contrastBorder));
}
@@ -208,8 +208,8 @@ export class PanelPart extends CompositePart<Panel> implements IPanelService {
return this.hideActiveComposite().then(composite => void 0);
}
protected createTitleLabel(parent: Builder): ICompositeTitleLabel {
const titleArea = this.compositeBar.create(parent.getHTMLElement());
protected createTitleLabel(parent: HTMLElement): ICompositeTitleLabel {
const titleArea = this.compositeBar.create(parent);
titleArea.classList.add('panel-switcher-container');
return {
@@ -27,6 +27,7 @@ import { contrastBorder } from 'vs/platform/theme/common/colorRegistry';
import { SIDE_BAR_TITLE_FOREGROUND, SIDE_BAR_BACKGROUND, SIDE_BAR_FOREGROUND, SIDE_BAR_BORDER } from 'vs/workbench/common/theme';
import { INotificationService } from 'vs/platform/notification/common/notification';
import { Dimension } from 'vs/base/browser/dom';
import { $ } from 'vs/base/browser/builder';
export class SidebarPart extends CompositePart<Viewlet> {
@@ -79,7 +80,7 @@ export class SidebarPart extends CompositePart<Viewlet> {
super.updateStyles();
// Part container
const container = this.getContainer();
const container = $(this.getContainer());
container.style('background-color', this.getColor(SIDE_BAR_BACKGROUND));
container.style('color', this.getColor(SIDE_BAR_FOREGROUND));
@@ -10,7 +10,7 @@ import * as nls from 'vs/nls';
import { toErrorMessage } from 'vs/base/common/errorMessage';
import { TPromise } from 'vs/base/common/winjs.base';
import { dispose, IDisposable } from 'vs/base/common/lifecycle';
import { Builder, $ } from 'vs/base/browser/builder';
import { $ } from 'vs/base/browser/builder';
import { OcticonLabel } from 'vs/base/browser/ui/octiconLabel/octiconLabel';
import { Registry } from 'vs/platform/registry/common/platform';
import { ICommandService } from 'vs/platform/commands/common/commands';
@@ -39,7 +39,7 @@ export class StatusbarPart extends Part implements IStatusbarService {
private static readonly PRIORITY_PROP = 'priority';
private static readonly ALIGNMENT_PROP = 'alignment';
private statusItemsContainer: Builder;
private statusItemsContainer: HTMLElement;
private statusMsgDispose: IDisposable;
private styleElement: HTMLStyleElement;
@@ -67,7 +67,7 @@ export class StatusbarPart extends Part implements IStatusbarService {
const toDispose = item.render(el);
// Insert according to priority
const container = this.statusItemsContainer.getHTMLElement();
const container = this.statusItemsContainer;
const neighbours = this.getEntries(alignment);
let inserted = false;
for (let i = 0; i < neighbours.length; i++) {
@@ -101,7 +101,7 @@ export class StatusbarPart extends Part implements IStatusbarService {
private getEntries(alignment: StatusbarAlignment): HTMLElement[] {
const entries: HTMLElement[] = [];
const container = this.statusItemsContainer.getHTMLElement();
const container = this.statusItemsContainer;
const children = container.children;
for (let i = 0; i < children.length; i++) {
const childElement = <HTMLElement>children.item(i);
@@ -113,8 +113,8 @@ export class StatusbarPart extends Part implements IStatusbarService {
return entries;
}
public createContentArea(parent: Builder): Builder {
this.statusItemsContainer = $(parent);
public createContentArea(parent: HTMLElement): HTMLElement {
this.statusItemsContainer = parent;
// Fill in initial items that were contributed from the registry
const registry = Registry.as<IStatusbarRegistry>(Extensions.Statusbar);
@@ -129,7 +129,7 @@ export class StatusbarPart extends Part implements IStatusbarService {
const el = this.doCreateStatusItem(descriptor.alignment, descriptor.priority);
const dispose = item.render(el);
this.statusItemsContainer.append(el);
this.statusItemsContainer.appendChild(el);
return dispose;
}));
@@ -140,7 +140,7 @@ export class StatusbarPart extends Part implements IStatusbarService {
protected updateStyles(): void {
super.updateStyles();
const container = this.getContainer();
const container = $(this.getContainer());
// Background colors
const backgroundColor = this.getColor(this.contextService.getWorkbenchState() !== WorkbenchState.EMPTY ? STATUS_BAR_BACKGROUND : STATUS_BAR_NO_FOLDER_BACKGROUND);
@@ -226,7 +226,7 @@ export class TitlebarPart extends Part implements ITitleService {
});
}
public createContentArea(parent: Builder): Builder {
public createContentArea(parent: HTMLElement): HTMLElement {
this.titleContainer = $(parent);
// Title
@@ -262,20 +262,19 @@ export class TitlebarPart extends Part implements ITitleService {
}, 0 /* need a timeout because we are in capture phase */);
}, void 0, true /* use capture to know the currently active element properly */);
return this.titleContainer;
return this.titleContainer.getHTMLElement();
}
protected updateStyles(): void {
super.updateStyles();
// Part container
const container = this.getContainer();
if (container) {
container.style('color', this.getColor(this.isInactive ? TITLE_BAR_INACTIVE_FOREGROUND : TITLE_BAR_ACTIVE_FOREGROUND));
container.style('background-color', this.getColor(this.isInactive ? TITLE_BAR_INACTIVE_BACKGROUND : TITLE_BAR_ACTIVE_BACKGROUND));
if (this.titleContainer) {
this.titleContainer.style('color', this.getColor(this.isInactive ? TITLE_BAR_INACTIVE_FOREGROUND : TITLE_BAR_ACTIVE_FOREGROUND));
this.titleContainer.style('background-color', this.getColor(this.isInactive ? TITLE_BAR_INACTIVE_BACKGROUND : TITLE_BAR_ACTIVE_BACKGROUND));
const titleBorder = this.getColor(TITLE_BAR_BORDER);
container.style('border-bottom', titleBorder ? `1px solid ${titleBorder}` : null);
this.titleContainer.style('border-bottom', titleBorder ? `1px solid ${titleBorder}` : null);
}
}
@@ -9,7 +9,6 @@ import { IDisposable, Disposable, dispose } from 'vs/base/common/lifecycle';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { TPromise } from 'vs/base/common/winjs.base';
import * as DOM from 'vs/base/browser/dom';
import { $ } from 'vs/base/browser/builder';
import { LIGHT, FileThemeIcon, FolderThemeIcon } from 'vs/platform/theme/common/themeService';
import { ITree, IDataSource, IRenderer, ContextMenuEvent } from 'vs/base/parts/tree/browser/tree';
import { TreeItemCollapsibleState, ITreeItem, ITreeViewer, ICustomViewsService, ITreeViewDataProvider, ViewsRegistry, IViewDescriptor, TreeViewItemHandleArg, ICustomViewDescriptor, IViewsViewlet } from 'vs/workbench/common/views';
@@ -171,9 +170,9 @@ class CustomTreeViewer extends Disposable implements ITreeViewer {
if (this.tree) {
if (this.isVisible) {
$(this.tree.getHTMLElement()).show();
DOM.show(this.tree.getHTMLElement());
} else {
$(this.tree.getHTMLElement()).hide(); // make sure the tree goes out of the tabindex world by hiding it
DOM.hide(this.tree.getHTMLElement()); // make sure the tree goes out of the tabindex world by hiding it
}
if (this.isVisible) {
@@ -10,8 +10,7 @@ import { Event, Emitter, filterEvent } from 'vs/base/common/event';
import { ColorIdentifier, contrastBorder } from 'vs/platform/theme/common/colorRegistry';
import { attachStyler, IColorMapping } from 'vs/platform/theme/common/styler';
import { SIDE_BAR_DRAG_AND_DROP_BACKGROUND, SIDE_BAR_SECTION_HEADER_FOREGROUND, SIDE_BAR_SECTION_HEADER_BACKGROUND } from 'vs/workbench/common/theme';
import { Builder } from 'vs/base/browser/builder';
import { append, $, trackFocus, toggleClass, EventType, isAncestor, Dimension } from 'vs/base/browser/dom';
import { append, $, trackFocus, toggleClass, EventType, isAncestor, Dimension, addDisposableListener } from 'vs/base/browser/dom';
import { IDisposable, combinedDisposable } from 'vs/base/common/lifecycle';
import { firstIndex } from 'vs/base/common/arrays';
import { IAction, IActionRunner } from 'vs/base/common/actions';
@@ -166,13 +165,12 @@ export class PanelViewlet extends Viewlet {
super(id, partService, telemetryService, themeService);
}
async create(parent: Builder): TPromise<void> {
async create(parent: HTMLElement): TPromise<void> {
super.create(parent);
const container = parent.getHTMLElement();
this.panelview = this._register(new PanelView(container, this.options));
this.panelview = this._register(new PanelView(parent, this.options));
this._register(this.panelview.onDidDrop(({ from, to }) => this.movePanel(from as ViewletPanel, to as ViewletPanel)));
this._register(parent.on(EventType.CONTEXT_MENU, (e: MouseEvent) => this.showContextMenu(new StandardMouseEvent(e))));
this._register(addDisposableListener(parent, EventType.CONTEXT_MENU, (e: MouseEvent) => this.showContextMenu(new StandardMouseEvent(e))));
}
private showContextMenu(event: StandardMouseEvent): void {
@@ -6,7 +6,6 @@
import { TPromise } from 'vs/base/common/winjs.base';
import * as errors from 'vs/base/common/errors';
import * as DOM from 'vs/base/browser/dom';
import { $, Builder } from 'vs/base/browser/builder';
import { Scope } from 'vs/workbench/common/memento';
import { dispose, IDisposable } from 'vs/base/common/lifecycle';
import { IAction, IActionRunner } from 'vs/base/common/actions';
@@ -137,9 +136,9 @@ export abstract class TreeViewsViewletPanel extends ViewsViewletPanel {
}
if (isVisible) {
$(tree.getHTMLElement()).show();
DOM.show(tree.getHTMLElement());
} else {
$(tree.getHTMLElement()).hide(); // make sure the tree goes out of the tabindex world by hiding it
DOM.hide(tree.getHTMLElement()); // make sure the tree goes out of the tabindex world by hiding it
}
if (isVisible) {
@@ -215,7 +214,7 @@ export class ViewsViewlet extends PanelViewlet implements IViewsViewlet {
this.viewletSettings = this.getMemento(storageService, Scope.WORKSPACE);
}
async create(parent: Builder): TPromise<void> {
async create(parent: HTMLElement): TPromise<void> {
await super.create(parent);
this._register(this.onDidSashChange(() => this.snapshotViewsStates()));
@@ -644,7 +643,7 @@ export class PersistentViewsViewlet extends ViewsViewlet {
this._register(this.onDidChangeViewVisibilityState(id => this.onViewVisibilityChanged(id)));
}
create(parent: Builder): TPromise<void> {
create(parent: HTMLElement): TPromise<void> {
this.loadViewsStates();
return super.create(parent);
}
+1 -1
View File
@@ -154,7 +154,7 @@ export class ToggleViewletAction extends Action {
const activeViewlet = this.viewletService.getActiveViewlet();
const activeElement = document.activeElement;
return activeViewlet && activeElement && DOM.isAncestor(activeElement, (<Viewlet>activeViewlet).getContainer().getHTMLElement());
return activeViewlet && activeElement && DOM.isAncestor(activeElement, (<Viewlet>activeViewlet).getContainer());
}
}
+26 -26
View File
@@ -44,21 +44,21 @@ export interface INotificationChangeEvent {
}
export class NotificationHandle implements INotificationHandle {
private readonly _onDidDispose: Emitter<void> = new Emitter();
private readonly _onDidClose: Emitter<void> = new Emitter();
constructor(private item: INotificationViewItem, private disposeItem: (item: INotificationViewItem) => void) {
constructor(private item: INotificationViewItem, private closeItem: (item: INotificationViewItem) => void) {
this.registerListeners();
}
private registerListeners(): void {
once(this.item.onDidDispose)(() => {
this._onDidDispose.fire();
this._onDidDispose.dispose();
once(this.item.onDidClose)(() => {
this._onDidClose.fire();
this._onDidClose.dispose();
});
}
public get onDidDispose(): Event<void> {
return this._onDidDispose.event;
public get onDidClose(): Event<void> {
return this._onDidClose.event;
}
public get progress(): INotificationProgress {
@@ -77,9 +77,9 @@ export class NotificationHandle implements INotificationHandle {
this.item.updateActions(actions);
}
public dispose(): void {
this.disposeItem(this.item);
this._onDidDispose.dispose();
public close(): void {
this.closeItem(this.item);
this._onDidClose.dispose();
}
}
@@ -117,7 +117,7 @@ export class NotificationsModel implements INotificationsModel {
// Deduplicate
const duplicate = this.findNotification(item);
if (duplicate) {
duplicate.dispose();
duplicate.close();
}
// Add to list as first entry
@@ -127,15 +127,15 @@ export class NotificationsModel implements INotificationsModel {
this._onDidNotificationChange.fire({ item, index: 0, kind: NotificationChangeType.ADD });
// Wrap into handle
return new NotificationHandle(item, item => this.disposeItem(item));
return new NotificationHandle(item, item => this.closeItem(item));
}
private disposeItem(item: INotificationViewItem): void {
private closeItem(item: INotificationViewItem): void {
const liveItem = this.findNotification(item);
if (liveItem && liveItem !== item) {
liveItem.dispose(); // item could have been replaced with another one, make sure to dispose the live item
liveItem.close(); // item could have been replaced with another one, make sure to close the live item
} else {
item.dispose(); // otherwise just dispose the item that was passed in
item.close(); // otherwise just close the item that was passed in
}
}
@@ -174,7 +174,7 @@ export class NotificationsModel implements INotificationsModel {
}
});
once(item.onDidDispose)(() => {
once(item.onDidClose)(() => {
itemExpansionChangeListener.dispose();
itemLabelChangeListener.dispose();
@@ -204,7 +204,7 @@ export interface INotificationViewItem {
readonly canCollapse: boolean;
readonly onDidExpansionChange: Event<void>;
readonly onDidDispose: Event<void>;
readonly onDidClose: Event<void>;
readonly onDidLabelChange: Event<INotificationViewItemLabelChangeEvent>;
expand(): void;
@@ -217,7 +217,7 @@ export interface INotificationViewItem {
updateMessage(message: NotificationMessage): void;
updateActions(actions?: INotificationActions): void;
dispose(): void;
close(): void;
equals(item: INotificationViewItem);
}
@@ -359,7 +359,7 @@ export class NotificationViewItem implements INotificationViewItem {
private _progress: NotificationViewItemProgress;
private readonly _onDidExpansionChange: Emitter<void>;
private readonly _onDidDispose: Emitter<void>;
private readonly _onDidClose: Emitter<void>;
private readonly _onDidLabelChange: Emitter<INotificationViewItemLabelChangeEvent>;
public static create(notification: INotification): INotificationViewItem {
@@ -435,8 +435,8 @@ export class NotificationViewItem implements INotificationViewItem {
this._onDidLabelChange = new Emitter<INotificationViewItemLabelChangeEvent>();
this.toDispose.push(this._onDidLabelChange);
this._onDidDispose = new Emitter<void>();
this.toDispose.push(this._onDidDispose);
this._onDidClose = new Emitter<void>();
this.toDispose.push(this._onDidClose);
}
private setActions(actions: INotificationActions): void {
@@ -464,8 +464,8 @@ export class NotificationViewItem implements INotificationViewItem {
return this._onDidLabelChange.event;
}
public get onDidDispose(): Event<void> {
return this._onDidDispose.event;
public get onDidClose(): Event<void> {
return this._onDidClose.event;
}
public get canCollapse(): boolean {
@@ -556,8 +556,8 @@ export class NotificationViewItem implements INotificationViewItem {
}
}
public dispose(): void {
this._onDidDispose.fire();
public close(): void {
this._onDidClose.fire();
this.toDispose = dispose(this.toDispose);
}
@@ -582,7 +582,7 @@ export class NotificationViewItem implements INotificationViewItem {
}
for (let i = 0; i < primaryActions.length; i++) {
if (primaryActions[i].id !== otherPrimaryActions[i].id) {
if ((primaryActions[i].id + primaryActions[i].label) !== (otherPrimaryActions[i].id + otherPrimaryActions[i].label)) {
return false;
}
}
+17 -16
View File
@@ -721,7 +721,7 @@ export class Workbench implements IPartService {
}
public getContainer(part: Parts): HTMLElement {
let container: Builder = null;
let container: HTMLElement = null;
switch (part) {
case Parts.TITLEBAR_PART:
container = this.titlebarPart.getContainer();
@@ -742,7 +742,8 @@ export class Workbench implements IPartService {
container = this.statusbarPart.getContainer();
break;
}
return container && container.getHTMLElement();
return container;
}
public isVisible(part: Parts): boolean {
@@ -942,10 +943,10 @@ export class Workbench implements IPartService {
this.sideBarPosition = position;
// Adjust CSS
this.activitybarPart.getContainer().removeClass(oldPositionValue);
this.sidebarPart.getContainer().removeClass(oldPositionValue);
this.activitybarPart.getContainer().addClass(newPositionValue);
this.sidebarPart.getContainer().addClass(newPositionValue);
DOM.removeClass(this.activitybarPart.getContainer(), oldPositionValue);
DOM.removeClass(this.sidebarPart.getContainer(), oldPositionValue);
DOM.addClass(this.activitybarPart.getContainer(), newPositionValue);
DOM.addClass(this.sidebarPart.getContainer(), newPositionValue);
// Update Styles
this.activitybarPart.updateStyles();
@@ -967,8 +968,8 @@ export class Workbench implements IPartService {
this.storageService.store(Workbench.panelPositionStorageKey, Position[this.panelPosition].toLowerCase(), StorageScope.WORKSPACE);
// Adjust CSS
this.panelPart.getContainer().removeClass(oldPositionValue);
this.panelPart.getContainer().addClass(newPositionValue);
DOM.removeClass(this.panelPart.getContainer(), oldPositionValue);
DOM.addClass(this.panelPart.getContainer(), newPositionValue);
// Update Styles
this.panelPart.updateStyles();
@@ -1108,10 +1109,10 @@ export class Workbench implements IPartService {
const editorContainer = this.editorPart.getContainer();
if (visibleEditors === 0) {
this.editorsVisibleContext.reset();
this.editorBackgroundDelayer.trigger(() => editorContainer.addClass('empty'));
this.editorBackgroundDelayer.trigger(() => DOM.addClass(editorContainer, 'empty'));
} else {
this.editorsVisibleContext.set(true);
this.editorBackgroundDelayer.trigger(() => editorContainer.removeClass('empty'));
this.editorBackgroundDelayer.trigger(() => DOM.removeClass(editorContainer, 'empty'));
}
}
@@ -1218,7 +1219,7 @@ export class Workbench implements IPartService {
role: 'contentinfo'
});
this.titlebarPart.create(titlebarContainer);
this.titlebarPart.create(titlebarContainer.getHTMLElement());
}
private createActivityBarPart(): void {
@@ -1229,7 +1230,7 @@ export class Workbench implements IPartService {
role: 'navigation'
});
this.activitybarPart.create(activitybarPartContainer);
this.activitybarPart.create(activitybarPartContainer.getHTMLElement());
}
private createSidebarPart(): void {
@@ -1240,7 +1241,7 @@ export class Workbench implements IPartService {
role: 'complementary'
});
this.sidebarPart.create(sidebarPartContainer);
this.sidebarPart.create(sidebarPartContainer.getHTMLElement());
}
private createPanelPart(): void {
@@ -1251,7 +1252,7 @@ export class Workbench implements IPartService {
role: 'complementary'
});
this.panelPart.create(panelPartContainer);
this.panelPart.create(panelPartContainer.getHTMLElement());
}
private createEditorPart(): void {
@@ -1262,7 +1263,7 @@ export class Workbench implements IPartService {
role: 'main'
});
this.editorPart.create(editorContainer);
this.editorPart.create(editorContainer.getHTMLElement());
}
private createStatusbarPart(): void {
@@ -1272,7 +1273,7 @@ export class Workbench implements IPartService {
role: 'contentinfo'
});
this.statusbarPart.create(statusbarContainer);
this.statusbarPart.create(statusbarContainer.getHTMLElement());
}
private createNotificationsHandlers(): void {

Some files were not shown because too many files have changed in this diff Show More