Merge from vscode 27ada910e121e23a6d95ecca9cae595fb98ab568

This commit is contained in:
ADS Merger
2020-04-30 00:53:43 +00:00
parent 87e5239713
commit 93f35ca321
413 changed files with 7190 additions and 8756 deletions
+4 -1
View File
@@ -209,7 +209,10 @@ export function renderMarkdown(markdown: IMarkdownString, options: MarkdownRende
'iframe': ['allowfullscreen', 'frameborder', 'src'],
'img': ['src', 'title', 'alt', 'width', 'height'],
'div': ['class', 'data-code'],
'span': ['class']
'span': ['class'],
// https://github.com/microsoft/vscode/issues/95937
'th': ['align'],
'td': ['align']
}
});
+5
View File
@@ -7,6 +7,8 @@ import 'vs/css!./aria';
import { isMacintosh } from 'vs/base/common/platform';
import * as dom from 'vs/base/browser/dom';
// Use a max length since we are inserting the whole msg in the DOM and that can cause browsers to freeze for long messages #94233
const MAX_MESSAGE_LENGTH = 20000;
let ariaContainer: HTMLElement;
let alertContainer: HTMLElement;
let statusContainer: HTMLElement;
@@ -54,6 +56,9 @@ function insertMessage(target: HTMLElement, msg: string): void {
}
dom.clearNode(target);
if (msg.length > MAX_MESSAGE_LENGTH) {
msg = msg.substr(0, MAX_MESSAGE_LENGTH);
}
target.textContent = msg;
// See https://www.paciellogroup.com/blog/2012/06/html5-accessibility-chops-aria-rolealert-browser-support/
+8 -1
View File
@@ -5,12 +5,14 @@
.monaco-text-button {
box-sizing: border-box;
display: inline-block;
display: flex;
width: 100%;
padding: 4px;
text-align: center;
cursor: pointer;
outline-offset: 2px !important;
justify-content: center;
align-items: center;
}
.monaco-text-button:hover {
@@ -21,3 +23,8 @@
opacity: 0.4;
cursor: default;
}
.monaco-button > .codicon {
margin: 0 0.2em;
color: inherit !important;
}
+9 -2
View File
@@ -12,9 +12,12 @@ import { mixin } from 'vs/base/common/objects';
import { Event as BaseEvent, Emitter } from 'vs/base/common/event';
import { Disposable } from 'vs/base/common/lifecycle';
import { Gesture, EventType } from 'vs/base/browser/touch';
import { renderCodicons } from 'vs/base/common/codicons';
import { escape } from 'vs/base/common/strings';
export interface IButtonOptions extends IButtonStyles {
title?: boolean | string;
readonly title?: boolean | string;
readonly supportCodicons?: boolean;
}
export interface IButtonStyles {
@@ -150,7 +153,11 @@ export class Button extends Disposable {
if (!DOM.hasClass(this._element, 'monaco-text-button')) {
DOM.addClass(this._element, 'monaco-text-button');
}
this._element.textContent = value;
if (this.options.supportCodicons) {
this._element.innerHTML = renderCodicons(escape(value));
} else {
this._element.textContent = value;
}
this._element.setAttribute('aria-label', value); // {{SQL CARBON EDIT}}
if (typeof this.options.title === 'string') {
this._element.title = this.options.title;
@@ -10,5 +10,6 @@
}
.codicon-animation-spin {
animation: codicon-spin 1.5s linear infinite;
/* Use steps to throttle FPS to reduce CPU usage */
animation: codicon-spin 1.5s steps(30) infinite;
}
+8 -2
View File
@@ -59,6 +59,7 @@ export interface IListViewOptions<T> {
readonly horizontalScrolling?: boolean;
readonly accessibilityProvider?: IListViewAccessibilityProvider<T>;
readonly additionalScrollHeight?: number;
readonly transformOptimization?: boolean;
}
const DefaultOptions = {
@@ -74,7 +75,8 @@ const DefaultOptions = {
onDragOver() { return false; },
drop() { }
},
horizontalScrolling: false
horizontalScrolling: false,
transformOptimization: true
};
export class ElementsDragAndDropData<T, TContext = void> implements IDragAndDropData {
@@ -275,7 +277,11 @@ export class ListView<T> implements ISpliceable<T>, IDisposable {
this.rowsContainer = document.createElement('div');
this.rowsContainer.className = 'monaco-list-rows';
this.rowsContainer.style.transform = 'translate3d(0px, 0px, 0px)';
if (options.transformOptimization) {
this.rowsContainer.style.transform = 'translate3d(0px, 0px, 0px)';
}
this.disposables.add(Gesture.addTarget(this.rowsContainer));
this.scrollableElement = this.disposables.add(new ScrollableElement(this.rowsContainer, {
@@ -838,6 +838,7 @@ export interface IListOptions<T> {
readonly mouseSupport?: boolean;
readonly horizontalScrolling?: boolean;
readonly additionalScrollHeight?: number;
readonly transformOptimization?: boolean;
}
export interface IListStyles {
+1 -1
View File
@@ -84,7 +84,7 @@ export class Menu extends ActionBar {
context: options.context,
actionRunner: options.actionRunner,
ariaLabel: options.ariaLabel,
triggerKeys: { keys: [KeyCode.Enter, ...(isMacintosh ? [KeyCode.Space] : [])], keyDown: true }
triggerKeys: { keys: [KeyCode.Enter, ...(isMacintosh || isLinux ? [KeyCode.Space] : [])], keyDown: true }
});
this.menuElement = menuElement;
+6 -1
View File
@@ -184,13 +184,18 @@ export abstract class Pane extends Disposable implements IView {
}
this._orientation = orientation;
if (this.header) {
this.updateHeader();
}
}
render(): void {
this.header = $('.pane-header');
append(this.element, this.header);
this.header.setAttribute('tabindex', '0');
this.header.setAttribute('role', 'toolbar');
// Use role button so the aria-expanded state gets read https://github.com/microsoft/vscode/issues/95996
this.header.setAttribute('role', 'button');
this.header.setAttribute('aria-label', this.ariaHeaderLabel);
this.renderHeader(this.header);
+2 -1
View File
@@ -61,5 +61,6 @@
}
.monaco-tl-twistie.codicon-tree-item-loading::before {
animation: codicon-spin 1.25s linear infinite;
/* Use steps to throttle FPS to reduce CPU usage */
animation: codicon-spin 1.25s steps(30) infinite;
}
+4 -4
View File
@@ -392,7 +392,7 @@ export function anyScore(pattern: string, lowPattern: string, _patternPos: numbe
//#region --- fuzzyScore ---
export function createMatches(score: undefined | FuzzyScore, offset = 0): IMatch[] {
export function createMatches(score: undefined | FuzzyScore): IMatch[] {
if (typeof score === 'undefined') {
return [];
}
@@ -404,10 +404,10 @@ export function createMatches(score: undefined | FuzzyScore, offset = 0): IMatch
for (let pos = wordStart; pos < _maxLen; pos++) {
if (matches[matches.length - (pos + 1)] === '1') {
const last = res[res.length - 1];
if (last && last.end === pos + offset) {
last.end = pos + offset + 1;
if (last && last.end === pos) {
last.end = pos + 1;
} else {
res.push({ start: pos + offset, end: pos + offset + 1 });
res.push({ start: pos, end: pos + 1 });
}
}
}
+43 -26
View File
@@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/
import { compareAnything } from 'vs/base/common/comparers';
import { matchesPrefix, IMatch, matchesCamelCase, isUpper, fuzzyScore, createMatches as createFuzzyMatches } from 'vs/base/common/filters';
import { matchesPrefix, IMatch, matchesCamelCase, isUpper, fuzzyScore, createMatches as createFuzzyMatches, matchesStrictPrefix } from 'vs/base/common/filters';
import { sep } from 'vs/base/common/path';
import { isWindows, isLinux } from 'vs/base/common/platform';
import { stripWildcards, equalsIgnoreCase } from 'vs/base/common/strings';
@@ -281,24 +281,24 @@ export type FuzzyScore2 = [number | undefined /* score */, IMatch[]];
const NO_SCORE2: FuzzyScore2 = [undefined, []];
export function scoreFuzzy2(target: string, query: IPreparedQuery | IPreparedQueryPiece, patternStart = 0, matchOffset = 0): FuzzyScore2 {
export function scoreFuzzy2(target: string, query: IPreparedQuery | IPreparedQueryPiece, patternStart = 0, wordStart = 0): FuzzyScore2 {
// Score: multiple inputs
const preparedQuery = query as IPreparedQuery;
if (preparedQuery.values && preparedQuery.values.length > 1) {
return doScoreFuzzy2Multiple(target, preparedQuery.values, patternStart, matchOffset);
return doScoreFuzzy2Multiple(target, preparedQuery.values, patternStart, wordStart);
}
// Score: single input
return doScoreFuzzy2Single(target, query, patternStart, matchOffset);
return doScoreFuzzy2Single(target, query, patternStart, wordStart);
}
function doScoreFuzzy2Multiple(target: string, query: IPreparedQueryPiece[], patternStart: number, matchOffset: number): FuzzyScore2 {
function doScoreFuzzy2Multiple(target: string, query: IPreparedQueryPiece[], patternStart: number, wordStart: number): FuzzyScore2 {
let totalScore = 0;
const totalMatches: IMatch[] = [];
for (const queryPiece of query) {
const [score, matches] = doScoreFuzzy2Single(target, queryPiece, patternStart, matchOffset);
const [score, matches] = doScoreFuzzy2Single(target, queryPiece, patternStart, wordStart);
if (typeof score !== 'number') {
// if a single query value does not match, return with
// no score entirely, we require all queries to match
@@ -314,13 +314,13 @@ function doScoreFuzzy2Multiple(target: string, query: IPreparedQueryPiece[], pat
return [totalScore, normalizeMatches(totalMatches)];
}
function doScoreFuzzy2Single(target: string, query: IPreparedQueryPiece, patternStart: number, matchOffset: number): FuzzyScore2 {
const score = fuzzyScore(query.original, query.originalLowercase, patternStart, target, target.toLowerCase(), 0, true);
function doScoreFuzzy2Single(target: string, query: IPreparedQueryPiece, patternStart: number, wordStart: number): FuzzyScore2 {
const score = fuzzyScore(query.original, query.originalLowercase, patternStart, target, target.toLowerCase(), wordStart, true);
if (!score) {
return NO_SCORE2;
}
return [score[0], createFuzzyMatches(score, matchOffset)];
return [score[0], createFuzzyMatches(score)];
}
//#endregion
@@ -370,9 +370,10 @@ export interface IItemAccessor<T> {
}
const PATH_IDENTITY_SCORE = 1 << 18;
const LABEL_PREFIX_SCORE = 1 << 17;
const LABEL_CAMELCASE_SCORE = 1 << 16;
const LABEL_SCORE_THRESHOLD = 1 << 15;
const LABEL_PREFIX_SCORE_MATCHCASE = 1 << 17;
const LABEL_PREFIX_SCORE_IGNORECASE = 1 << 16;
const LABEL_CAMELCASE_SCORE = 1 << 15;
const LABEL_SCORE_THRESHOLD = 1 << 14;
export function scoreItemFuzzy<T>(item: T, query: IPreparedQuery, fuzzy: boolean, accessor: IItemAccessor<T>, cache: FuzzyScorerCache): IItemScore {
if (!item || !query.normalized) {
@@ -458,13 +459,14 @@ function doScoreItemFuzzySingle(label: string, description: string | undefined,
// Prefer label matches if told so
if (preferLabelMatches) {
// Treat prefix matches on the label second highest
const prefixLabelMatch = matchesPrefix(query.normalized, label);
if (prefixLabelMatch) {
return { score: LABEL_PREFIX_SCORE, labelMatch: prefixLabelMatch };
// Treat prefix matches on the label highest
const prefixLabelMatchIgnoreCase = matchesPrefix(query.normalized, label);
if (prefixLabelMatchIgnoreCase) {
const prefixLabelMatchStrictCase = matchesStrictPrefix(query.normalized, label);
return { score: prefixLabelMatchStrictCase ? LABEL_PREFIX_SCORE_MATCHCASE : LABEL_PREFIX_SCORE_IGNORECASE, labelMatch: prefixLabelMatchStrictCase || prefixLabelMatchIgnoreCase };
}
// Treat camelcase matches on the label third highest
// Treat camelcase matches on the label second highest
const camelcaseLabelMatch = matchesCamelCase(query.normalized, label);
if (camelcaseLabelMatch) {
return { score: LABEL_CAMELCASE_SCORE, labelMatch: camelcaseLabelMatch };
@@ -600,10 +602,10 @@ export function compareItemsByFuzzyScore<T>(itemA: T, itemB: T, query: IPrepared
}
}
// 2.) prefer label prefix matches
if (scoreA === LABEL_PREFIX_SCORE || scoreB === LABEL_PREFIX_SCORE) {
// 2.) prefer label prefix matches (match case)
if (scoreA === LABEL_PREFIX_SCORE_MATCHCASE || scoreB === LABEL_PREFIX_SCORE_MATCHCASE) {
if (scoreA !== scoreB) {
return scoreA === LABEL_PREFIX_SCORE ? -1 : 1;
return scoreA === LABEL_PREFIX_SCORE_MATCHCASE ? -1 : 1;
}
const labelA = accessor.getItemLabel(itemA) || '';
@@ -615,7 +617,22 @@ export function compareItemsByFuzzyScore<T>(itemA: T, itemB: T, query: IPrepared
}
}
// 3.) prefer camelcase matches
// 3.) prefer label prefix matches (ignore case)
if (scoreA === LABEL_PREFIX_SCORE_IGNORECASE || scoreB === LABEL_PREFIX_SCORE_IGNORECASE) {
if (scoreA !== scoreB) {
return scoreA === LABEL_PREFIX_SCORE_IGNORECASE ? -1 : 1;
}
const labelA = accessor.getItemLabel(itemA) || '';
const labelB = accessor.getItemLabel(itemB) || '';
// prefer shorter names when both match on label prefix
if (labelA.length !== labelB.length) {
return labelA.length - labelB.length;
}
}
// 4.) prefer camelcase matches
if (scoreA === LABEL_CAMELCASE_SCORE || scoreB === LABEL_CAMELCASE_SCORE) {
if (scoreA !== scoreB) {
return scoreA === LABEL_CAMELCASE_SCORE ? -1 : 1;
@@ -636,7 +653,7 @@ export function compareItemsByFuzzyScore<T>(itemA: T, itemB: T, query: IPrepared
}
}
// 4.) prefer label scores
// 5.) prefer label scores
if (scoreA > LABEL_SCORE_THRESHOLD || scoreB > LABEL_SCORE_THRESHOLD) {
if (scoreB < LABEL_SCORE_THRESHOLD) {
return -1;
@@ -647,12 +664,12 @@ export function compareItemsByFuzzyScore<T>(itemA: T, itemB: T, query: IPrepared
}
}
// 5.) compare by score
// 6.) compare by score
if (scoreA !== scoreB) {
return scoreA > scoreB ? -1 : 1;
}
// 6.) prefer matches in label over non-label matches
// 7.) prefer matches in label over non-label matches
const itemAHasLabelMatches = Array.isArray(itemScoreA.labelMatch) && itemScoreA.labelMatch.length > 0;
const itemBHasLabelMatches = Array.isArray(itemScoreB.labelMatch) && itemScoreB.labelMatch.length > 0;
if (itemAHasLabelMatches && !itemBHasLabelMatches) {
@@ -661,14 +678,14 @@ export function compareItemsByFuzzyScore<T>(itemA: T, itemB: T, query: IPrepared
return 1;
}
// 7.) scores are identical, prefer more compact matches (label and description)
// 8.) scores are identical, prefer more compact matches (label and description)
const itemAMatchDistance = computeLabelAndDescriptionMatchDistance(itemA, itemScoreA, accessor);
const itemBMatchDistance = computeLabelAndDescriptionMatchDistance(itemB, itemScoreB, accessor);
if (itemAMatchDistance && itemBMatchDistance && itemAMatchDistance !== itemBMatchDistance) {
return itemBMatchDistance > itemAMatchDistance ? -1 : 1;
}
// 7.) at this point, scores are identical and match compactness as well
// 9.) at this point, scores are identical and match compactness as well
// for both items so we start to use the fallback compare
return fallbackCompare(itemA, itemB, query, accessor);
}
+12 -11
View File
@@ -53,17 +53,18 @@ export interface IJSONSchema {
then?: IJSONSchema;
else?: IJSONSchema;
// VSCode extensions
defaultSnippets?: IJSONSchemaSnippet[]; // VSCode extension
errorMessage?: string; // VSCode extension
patternErrorMessage?: string; // VSCode extension
deprecationMessage?: string; // VSCode extension
enumDescriptions?: string[]; // VSCode extension
markdownEnumDescriptions?: string[]; // VSCode extension
markdownDescription?: string; // VSCode extension
doNotSuggest?: boolean; // VSCode extension
allowComments?: boolean; // VSCode extension
allowTrailingCommas?: boolean; // VSCode extension
// VS Code extensions
defaultSnippets?: IJSONSchemaSnippet[];
errorMessage?: string;
patternErrorMessage?: string;
deprecationMessage?: string;
markdownDeprecationMessage?: string;
enumDescriptions?: string[];
markdownEnumDescriptions?: string[];
markdownDescription?: string;
doNotSuggest?: boolean;
allowComments?: boolean;
allowTrailingCommas?: boolean;
}
export interface IJSONSchemaMap {
@@ -853,7 +853,7 @@ class QuickPick<T extends IQuickPickItem> extends QuickInput implements IQuickPi
}
dom.toggleClass(this.ui.container, 'hidden-input', hideInput);
const visibilities: Visibilities = {
title: !!this.title || !!this.step,
title: !!this.title || !!this.step || !!this.buttons.length,
description: !!this.description,
checkAll: this.canSelectMany,
inputBox: !hideInput,
@@ -1048,7 +1048,12 @@ class InputBox extends QuickInput implements IInputBox {
if (!this.visible) {
return;
}
this.ui.setVisibilities({ title: !!this.title || !!this.step, description: !!this.description || !!this.step, inputBox: true, message: true });
const visibilities: Visibilities = {
title: !!this.title || !!this.step || !!this.buttons.length,
description: !!this.description || !!this.step,
inputBox: true, message: true
};
this.ui.setVisibilities(visibilities);
super.update();
if (this.ui.inputBox.value !== this.value) {
this.ui.inputBox.value = this.value;
+15 -2
View File
@@ -912,6 +912,19 @@ suite('Fuzzy Scorer', () => {
assert.equal(res[0], resourceB);
});
test('compareFilesByScore - prefer case match (bug #96122)', function () {
const resourceA = URI.file('lists.php');
const resourceB = URI.file('lib/Lists.php');
let query = 'Lists.php';
let res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));
assert.equal(res[0], resourceB);
res = [resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor));
assert.equal(res[0], resourceB);
});
test('prepareQuery', () => {
assert.equal(scorer.prepareQuery(' f*a ').normalized, 'fa');
assert.equal(scorer.prepareQuery('model Tester.ts').original, 'model Tester.ts');
@@ -977,14 +990,14 @@ suite('Fuzzy Scorer', () => {
const target = 'HeLlo-World';
for (const offset of [0, 3]) {
let [score, matches] = _doScore2(target, 'HeLlo-World', offset);
let [score, matches] = _doScore2(offset === 0 ? target : `123${target}`, 'HeLlo-World', offset);
assert.ok(score);
assert.equal(matches.length, 1);
assert.equal(matches[0].start, 0 + offset);
assert.equal(matches[0].end, target.length + offset);
[score, matches] = _doScore2(target, 'HW', offset);
[score, matches] = _doScore2(offset === 0 ? target : `123${target}`, 'HW', offset);
assert.ok(score);
assert.equal(matches.length, 2);
@@ -28,7 +28,7 @@
baseUrl: `${window.location.origin}/static/out`,
paths: {
'vscode-textmate': `${window.location.origin}/static/remote/web/node_modules/vscode-textmate/release/main`,
'onigasm-umd': `${window.location.origin}/static/remote/web/node_modules/onigasm-umd/release/main`,
'vscode-oniguruma': `${window.location.origin}/static/remote/web/node_modules/vscode-oniguruma/release/main`,
'xterm': `${window.location.origin}/static/remote/web/node_modules/xterm/lib/xterm.js`,
'xterm-addon-search': `${window.location.origin}/static/remote/web/node_modules/xterm-addon-search/lib/xterm-addon-search.js`,
'xterm-addon-unicode11': `${window.location.origin}/static/remote/web/node_modules/xterm-addon-unicode11/lib/xterm-addon-unicode11.js`,
+1 -1
View File
@@ -32,7 +32,7 @@
baseUrl: `${window.location.origin}/static/out`,
paths: {
'vscode-textmate': `${window.location.origin}/static/node_modules/vscode-textmate/release/main`,
'onigasm-umd': `${window.location.origin}/static/node_modules/onigasm-umd/release/main`,
'vscode-oniguruma': `${window.location.origin}/static/node_modules/vscode-oniguruma/release/main`,
'xterm': `${window.location.origin}/static/node_modules/xterm/lib/xterm.js`,
'xterm-addon-search': `${window.location.origin}/static/node_modules/xterm-addon-search/lib/xterm-addon-search.js`,
'xterm-addon-unicode11': `${window.location.origin}/static/node_modules/xterm-addon-unicode11/lib/xterm-addon-unicode11.js`,
@@ -341,7 +341,7 @@ export class IssueReporter extends Disposable {
const appender = combinedAppender(new TelemetryAppenderClient(channel), new LogAppender(logService));
const commonProperties = resolveCommonProperties(product.commit || 'Commit unknown', product.version, configuration.machineId, product.msftInternalDomains, this.environmentService.installSourcePath);
const piiPaths = this.environmentService.extensionsPath ? [this.environmentService.appRoot, this.environmentService.extensionsPath] : [this.environmentService.appRoot];
const config: ITelemetryServiceConfig = { appender, commonProperties, piiPaths };
const config: ITelemetryServiceConfig = { appender, commonProperties, piiPaths, sendErrorTelemetry: true };
const telemetryService = instantiationService.createInstance(TelemetryService, config);
this._register(telemetryService);
@@ -716,7 +716,7 @@ export class IssueReporter extends Disposable {
type IssueReporterSearchError = {
message: string;
};
this.telemetryService.publicLog2<IssueReporterSearchError, IssueReporterSearchErrorClassification>('issueReporterSearchError', { message: error.message }, true);
this.telemetryService.publicLogError2<IssueReporterSearchError, IssueReporterSearchErrorClassification>('issueReporterSearchError', { message: error.message });
}
private setUpTypes(): void {
@@ -176,6 +176,7 @@ async function main(server: Server, initData: ISharedProcessInitData, configurat
const config: ITelemetryServiceConfig = {
appender: combinedAppender(appInsightsAppender, new LogAppender(logService)),
commonProperties: resolveCommonProperties(product.commit, product.version, configuration.machineId, product.msftInternalDomains, installSourcePath),
sendErrorTelemetry: true,
piiPaths: extensionsPath ? [appRoot, extensionsPath] : [appRoot]
};
@@ -3,7 +3,6 @@
<html>
<head>
<meta charset="utf-8" />
<!-- {{SQL CARBON EDIT}} @anthonydresser add 'unsafe-eval' under script src; since its required by angular -->
<meta http-equiv="Content-Security-Policy" content="default-src 'none'; img-src 'self' https: data: blob: vscode-remote-resource:; media-src 'none'; frame-src 'self' https://*.vscode-webview-test.com; object-src 'self'; script-src 'self' 'unsafe-eval'; style-src 'self' 'unsafe-inline'; connect-src 'self' https:; font-src 'self' https: vscode-remote-resource:;">
</head>
<body class="vs-dark" aria-label="">
+2 -2
View File
@@ -193,7 +193,7 @@ export class CodeApplication extends Disposable {
return;
}
delete (webPreferences as { preloadURL: string }).preloadURL; // https://github.com/electron/electron/issues/21553
delete (webPreferences as { preloadURL: string | undefined }).preloadURL; // https://github.com/electron/electron/issues/21553
// Otherwise prevent loading
this.logService.error('webContents#web-contents-created: Prevented webview attach');
@@ -489,7 +489,7 @@ export class CodeApplication extends Disposable {
const appender = combinedAppender(new TelemetryAppenderClient(channel), new LogAppender(this.logService));
const commonProperties = resolveCommonProperties(product.commit, product.version, machineId, product.msftInternalDomains, this.environmentService.installSourcePath);
const piiPaths = this.environmentService.extensionsPath ? [this.environmentService.appRoot, this.environmentService.extensionsPath] : [this.environmentService.appRoot];
const config: ITelemetryServiceConfig = { appender, commonProperties, piiPaths, trueMachineId };
const config: ITelemetryServiceConfig = { appender, commonProperties, piiPaths, trueMachineId, sendErrorTelemetry: true };
services.set(ITelemetryService, new SyncDescriptor(TelemetryService, [config]));
} else {
+3 -3
View File
@@ -746,11 +746,11 @@ export class CodeWindow extends Disposable implements ICodeWindow {
// Config (combination of process.argv and window configuration)
const environment = parseArgs(process.argv, OPTIONS);
const config = Object.assign(environment, windowConfiguration);
const config = Object.assign(environment, windowConfiguration) as unknown as { [key: string]: unknown };
for (const key in config) {
const configValue = (config as any)[key];
const configValue = config[key];
if (configValue === undefined || configValue === null || configValue === '' || configValue === false) {
delete (config as any)[key]; // only send over properties that have a true value
delete config[key]; // only send over properties that have a true value
}
}
+1
View File
@@ -345,6 +345,7 @@ export async function main(argv: ParsedArgs): Promise<void> {
const config: ITelemetryServiceConfig = {
appender: combinedAppender(...appenders),
sendErrorTelemetry: false,
commonProperties: resolveCommonProperties(product.commit, product.version, stateService.getItem('telemetry.machineId'), product.msftInternalDomains, installSourcePath),
piiPaths: extensionsPath ? [appRoot, extensionsPath] : [appRoot]
};
+13 -4
View File
@@ -6,7 +6,7 @@
import * as strings from 'vs/base/common/strings';
import { ICodeEditor, IActiveCodeEditor } from 'vs/editor/browser/editorBrowser';
import { Position } from 'vs/editor/common/core/position';
import { Range } from 'vs/editor/common/core/range';
import { Range, IRange } from 'vs/editor/common/core/range';
import { CancellationTokenSource, CancellationToken } from 'vs/base/common/cancellation';
import { IDisposable, DisposableStore } from 'vs/base/common/lifecycle';
import { ITextModel } from 'vs/editor/common/model';
@@ -87,19 +87,28 @@ export class EditorState {
/**
* A cancellation token source that cancels when the editor changes as expressed
* by the provided flags
* @param range If provided, changes in position and selection within this range will not trigger cancellation
*/
export class EditorStateCancellationTokenSource extends EditorKeybindingCancellationTokenSource implements IDisposable {
private readonly _listener = new DisposableStore();
constructor(readonly editor: IActiveCodeEditor, flags: CodeEditorStateFlag, parent?: CancellationToken) {
constructor(readonly editor: IActiveCodeEditor, flags: CodeEditorStateFlag, range?: IRange, parent?: CancellationToken) {
super(editor, parent);
if (flags & CodeEditorStateFlag.Position) {
this._listener.add(editor.onDidChangeCursorPosition(_ => this.cancel()));
this._listener.add(editor.onDidChangeCursorPosition(e => {
if (!range || !Range.containsPosition(range, e.position)) {
this.cancel();
}
}));
}
if (flags & CodeEditorStateFlag.Selection) {
this._listener.add(editor.onDidChangeCursorSelection(_ => this.cancel()));
this._listener.add(editor.onDidChangeCursorSelection(e => {
if (!range || !Range.containsRange(range, e.selection)) {
this.cancel();
}
}));
}
if (flags & CodeEditorStateFlag.Scroll) {
this._listener.add(editor.onDidScrollChange(_ => this.cancel()));
@@ -27,11 +27,6 @@
white-space: nowrap;
}
.monaco-editor.vs-dark.mac .view-lines,
.monaco-editor.hc-black.mac .view-lines {
cursor: -webkit-image-set(url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAQAAAC1+jfqAAAAL0lEQVQoz2NgCD3x//9/BhBYBWdhgFVAiVW4JBFKGIa4AqD0//9D3pt4I4tAdAMAHTQ/j5Zom30AAAAASUVORK5CYII=') 1x, url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAQAAADZc7J/AAAAz0lEQVRIx2NgYGBY/R8I/vx5eelX3n82IJ9FxGf6tksvf/8FiTMQAcAGQMDvSwu09abffY8QYSAScNk45G198eX//yev73/4///701eh//kZSARckrNBRvz//+8+6ZohwCzjGNjdgQxkAg7B9WADeBjIBqtJCbhRA0YNoIkBSNmaPEMoNmA0FkYNoFKhapJ6FGyAH3nauaSmPfwI0v/3OukVi0CIZ+F25KrtYcx/CTIy0e+rC7R1Z4KMICVTQQ14feVXIbR695u14+Ir4gwAAD49E54wc1kWAAAAAElFTkSuQmCC') 2x) 5 8, text;
}
.monaco-editor .view-line {
position: absolute;
width: 100%;
@@ -10,7 +10,7 @@ import { ViewPart } from 'vs/editor/browser/view/viewPart';
import { Position } from 'vs/editor/common/core/position';
import { IConfiguration } from 'vs/editor/common/editorCommon';
import { TokenizationRegistry } from 'vs/editor/common/modes';
import { editorCursorForeground, editorOverviewRulerBorder } from 'vs/editor/common/view/editorColorRegistry';
import { editorCursorForeground, editorOverviewRulerBorder, editorOverviewRulerBackground } from 'vs/editor/common/view/editorColorRegistry';
import { RenderingContext, RestrictedRenderingContext } from 'vs/editor/common/view/renderingContext';
import { ViewContext, EditorTheme } from 'vs/editor/common/view/viewContext';
import * as viewEvents from 'vs/editor/common/view/viewEvents';
@@ -60,7 +60,10 @@ class Settings {
const minimapOpts = options.get(EditorOption.minimap);
const minimapEnabled = minimapOpts.enabled;
const minimapSide = minimapOpts.side;
const backgroundColor = (minimapEnabled ? TokenizationRegistry.getDefaultBackground() : null);
const backgroundColor = minimapEnabled
? theme.getColor(editorOverviewRulerBackground) || TokenizationRegistry.getDefaultBackground()
: null;
if (backgroundColor === null || minimapSide === 'left') {
this.backgroundColor = null;
} else {
@@ -38,7 +38,7 @@ import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection';
import { INotificationService } from 'vs/platform/notification/common/notification';
import { defaultInsertColor, defaultRemoveColor, diffBorder, diffInserted, diffInsertedOutline, diffRemoved, diffRemovedOutline, scrollbarShadow, scrollbarSliderBackground, scrollbarSliderHoverBackground, scrollbarSliderActiveBackground } from 'vs/platform/theme/common/colorRegistry';
import { defaultInsertColor, defaultRemoveColor, diffBorder, diffInserted, diffInsertedOutline, diffRemoved, diffRemovedOutline, scrollbarShadow, scrollbarSliderBackground, scrollbarSliderHoverBackground, scrollbarSliderActiveBackground, diffDiagonalFill } from 'vs/platform/theme/common/colorRegistry';
import { IColorTheme, IThemeService, getThemeTypeSelector, registerThemingParticipant } from 'vs/platform/theme/common/themeService';
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
import { IDiffLinesChange, InlineDiffMargin } from 'vs/editor/browser/widget/inlineDiffMargin';
@@ -2262,4 +2262,17 @@ registerThemingParticipant((theme, collector) => {
`);
}
const diffDiagonalFillColor = theme.getColor(diffDiagonalFill);
collector.addRule(`
.monaco-editor .diagonal-fill {
background-image: linear-gradient(
-45deg,
${diffDiagonalFillColor} 12.5%,
#0000 12.5%, #0000 50%,
${diffDiagonalFillColor} 50%, ${diffDiagonalFillColor} 62.5%,
#0000 62.5%, #0000 100%
);
background-size: 8px 8px;
}
`);
});
+6 -1
View File
@@ -114,6 +114,7 @@ export class DiffReview extends Disposable {
this._content = createFastDomNode(document.createElement('div'));
this._content.setClassName('diff-review-content');
this._content.setAttribute('role', 'code');
this.scrollbar = this._register(new DomScrollableElement(this._content.domNode, {}));
this.domNode.domNode.appendChild(this.scrollbar.getDomNode());
@@ -748,7 +749,11 @@ export class DiffReview extends Disposable {
let ariaLabel: string = '';
switch (type) {
case DiffEntryType.Equal:
ariaLabel = nls.localize('equalLine', "{0} original line {1} modified line {2}", lineContent, originalLine, modifiedLine);
if (originalLine === modifiedLine) {
ariaLabel = nls.localize('unchangedLine', "{0} unchanged line {1}", lineContent, originalLine);
} else {
ariaLabel = nls.localize('equalLine', "{0} original line {1} modified line {2}", lineContent, originalLine, modifiedLine);
}
break;
case DiffEntryType.Insert:
ariaLabel = nls.localize('insertLine', "+ {0} modified line {1}", lineContent, modifiedLine);
Binary file not shown.

Before

Width:  |  Height:  |  Size: 185 B

@@ -51,16 +51,6 @@
text-align: right;
}
.monaco-editor .diagonal-fill {
background: url('diagonal-fill.png');
}
.monaco-editor.vs-dark .diagonal-fill {
opacity: 0.2;
}
.monaco-editor.hc-black .diagonal-fill {
background: none;
}
/* ---------- Inline Diff ---------- */
.monaco-editor .view-zones .view-lines .view-line span {
+12 -7
View File
@@ -103,14 +103,14 @@ export class ShiftCommand implements ICommand {
const { tabSize, indentSize, insertSpaces } = this._opts;
const shouldIndentEmptyLines = (startLine === endLine);
// if indenting or outdenting on a whitespace only line
if (this._selection.isEmpty()) {
if (/^\s*$/.test(model.getLineContent(startLine))) {
this._useLastEditRangeForCursorEndPosition = true;
}
}
if (this._opts.useTabStops) {
// if indenting or outdenting on a whitespace only line
if (this._selection.isEmpty()) {
if (/^\s*$/.test(model.getLineContent(startLine))) {
this._useLastEditRangeForCursorEndPosition = true;
}
}
// keep track of previous line's "miss-alignment"
let previousLineExtraSpaces = 0, extraSpaces = 0;
for (let lineNumber = startLine; lineNumber <= endLine; lineNumber++, previousLineExtraSpaces = extraSpaces) {
@@ -188,6 +188,11 @@ export class ShiftCommand implements ICommand {
}
} else {
// if indenting or outdenting on a whitespace only line
if (!this._opts.isUnshift && this._selection.isEmpty() && model.getLineLength(startLine) === 0) {
this._useLastEditRangeForCursorEndPosition = true;
}
const oneIndent = (insertSpaces ? cachedStringRepeat(' ', indentSize) : '\t');
for (let lineNumber = startLine; lineNumber <= endLine; lineNumber++) {
+70 -31
View File
@@ -34,7 +34,6 @@ import { withUndefinedAsNull } from 'vs/base/common/types';
import { VSBufferReadableStream, VSBuffer } from 'vs/base/common/buffer';
import { TokensStore, MultilineTokens, countEOL, MultilineTokens2, TokensStore2 } from 'vs/editor/common/model/tokensStore';
import { Color } from 'vs/base/common/color';
import { Constants } from 'vs/base/common/uint';
import { EditorTheme } from 'vs/editor/common/view/viewContext';
import { IUndoRedoService } from 'vs/platform/undoRedo/common/undoRedo';
import { TextChange } from 'vs/editor/common/model/textChange';
@@ -172,6 +171,21 @@ const enum StringOffsetValidationType {
SurrogatePairs = 1,
}
type ContinueBracketSearchPredicate = null | (() => boolean);
class BracketSearchCanceled {
public static INSTANCE = new BracketSearchCanceled();
_searchCanceledBrand = undefined;
private constructor() { }
}
function stripBracketSearchCanceled<T>(result: T | null | BracketSearchCanceled): T | null {
if (result instanceof BracketSearchCanceled) {
return null;
}
return result;
}
export class TextModel extends Disposable implements model.ITextModel {
private static readonly MODEL_SYNC_LIMIT = 50 * 1024 * 1024; // 50 MB
@@ -2014,7 +2028,7 @@ export class TextModel extends Disposable implements model.ITextModel {
return null;
}
return this._findMatchingBracketUp(data, position);
return stripBracketSearchCanceled(this._findMatchingBracketUp(data, position, null));
}
public matchBracket(position: IPosition): [Range, Range] | null {
@@ -2062,8 +2076,11 @@ export class TextModel extends Disposable implements model.ITextModel {
// check that we didn't hit a bracket too far away from position
if (foundBracket.startColumn <= position.column && position.column <= foundBracket.endColumn) {
const foundBracketText = lineText.substring(foundBracket.startColumn - 1, foundBracket.endColumn - 1).toLowerCase();
const r = this._matchFoundBracket(foundBracket, currentModeBrackets.textIsBracket[foundBracketText], currentModeBrackets.textIsOpenBracket[foundBracketText]);
const r = this._matchFoundBracket(foundBracket, currentModeBrackets.textIsBracket[foundBracketText], currentModeBrackets.textIsOpenBracket[foundBracketText], null);
if (r) {
if (r instanceof BracketSearchCanceled) {
return null;
}
bestResult = r;
}
}
@@ -2100,8 +2117,11 @@ export class TextModel extends Disposable implements model.ITextModel {
// check that we didn't hit a bracket too far away from position
if (foundBracket && foundBracket.startColumn <= position.column && position.column <= foundBracket.endColumn) {
const foundBracketText = lineText.substring(foundBracket.startColumn - 1, foundBracket.endColumn - 1).toLowerCase();
const r = this._matchFoundBracket(foundBracket, prevModeBrackets.textIsBracket[foundBracketText], prevModeBrackets.textIsOpenBracket[foundBracketText]);
const r = this._matchFoundBracket(foundBracket, prevModeBrackets.textIsBracket[foundBracketText], prevModeBrackets.textIsOpenBracket[foundBracketText], null);
if (r) {
if (r instanceof BracketSearchCanceled) {
return null;
}
return r;
}
}
@@ -2111,35 +2131,41 @@ export class TextModel extends Disposable implements model.ITextModel {
return null;
}
private _matchFoundBracket(foundBracket: Range, data: RichEditBracket, isOpen: boolean): [Range, Range] | null {
private _matchFoundBracket(foundBracket: Range, data: RichEditBracket, isOpen: boolean, continueSearchPredicate: ContinueBracketSearchPredicate): [Range, Range] | null | BracketSearchCanceled {
if (!data) {
return null;
}
if (isOpen) {
let matched = this._findMatchingBracketDown(data, foundBracket.getEndPosition());
if (matched) {
return [foundBracket, matched];
}
} else {
let matched = this._findMatchingBracketUp(data, foundBracket.getStartPosition());
if (matched) {
return [foundBracket, matched];
}
const matched = (
isOpen
? this._findMatchingBracketDown(data, foundBracket.getEndPosition(), continueSearchPredicate)
: this._findMatchingBracketUp(data, foundBracket.getStartPosition(), continueSearchPredicate)
);
if (!matched) {
return null;
}
return null;
if (matched instanceof BracketSearchCanceled) {
return matched;
}
return [foundBracket, matched];
}
private _findMatchingBracketUp(bracket: RichEditBracket, position: Position): Range | null {
private _findMatchingBracketUp(bracket: RichEditBracket, position: Position, continueSearchPredicate: ContinueBracketSearchPredicate): Range | null | BracketSearchCanceled {
// console.log('_findMatchingBracketUp: ', 'bracket: ', JSON.stringify(bracket), 'startPosition: ', String(position));
const languageId = bracket.languageIdentifier.id;
const reversedBracketRegex = bracket.reversedRegex;
let count = -1;
const searchPrevMatchingBracketInRange = (lineNumber: number, lineText: string, searchStartOffset: number, searchEndOffset: number): Range | null => {
let totalCallCount = 0;
const searchPrevMatchingBracketInRange = (lineNumber: number, lineText: string, searchStartOffset: number, searchEndOffset: number): Range | null | BracketSearchCanceled => {
while (true) {
if (continueSearchPredicate && (++totalCallCount) % 100 === 0 && !continueSearchPredicate()) {
return BracketSearchCanceled.INSTANCE;
}
const r = BracketsUtils.findPrevBracketInRange(reversedBracketRegex, lineNumber, lineText, searchStartOffset, searchEndOffset);
if (!r) {
break;
@@ -2214,15 +2240,19 @@ export class TextModel extends Disposable implements model.ITextModel {
return null;
}
private _findMatchingBracketDown(bracket: RichEditBracket, position: Position): Range | null {
private _findMatchingBracketDown(bracket: RichEditBracket, position: Position, continueSearchPredicate: ContinueBracketSearchPredicate): Range | null | BracketSearchCanceled {
// console.log('_findMatchingBracketDown: ', 'bracket: ', JSON.stringify(bracket), 'startPosition: ', String(position));
const languageId = bracket.languageIdentifier.id;
const bracketRegex = bracket.forwardRegex;
let count = 1;
const searchNextMatchingBracketInRange = (lineNumber: number, lineText: string, searchStartOffset: number, searchEndOffset: number): Range | null => {
let totalCallCount = 0;
const searchNextMatchingBracketInRange = (lineNumber: number, lineText: string, searchStartOffset: number, searchEndOffset: number): Range | null | BracketSearchCanceled => {
while (true) {
if (continueSearchPredicate && (++totalCallCount) % 100 === 0 && !continueSearchPredicate()) {
return BracketSearchCanceled.INSTANCE;
}
const r = BracketsUtils.findNextBracketInRange(bracketRegex, lineNumber, lineText, searchStartOffset, searchEndOffset);
if (!r) {
break;
@@ -2452,7 +2482,16 @@ export class TextModel extends Disposable implements model.ITextModel {
return null;
}
public findEnclosingBrackets(_position: IPosition, maxDuration = Constants.MAX_SAFE_SMALL_INTEGER): [Range, Range] | null {
public findEnclosingBrackets(_position: IPosition, maxDuration?: number): [Range, Range] | null {
let continueSearchPredicate: ContinueBracketSearchPredicate;
if (typeof maxDuration === 'undefined') {
continueSearchPredicate = null;
} else {
const startTime = Date.now();
continueSearchPredicate = () => {
return (Date.now() - startTime <= maxDuration);
};
}
const position = this.validatePosition(_position);
const lineCount = this.getLineCount();
const savedCounts = new Map<number, number[]>();
@@ -2468,8 +2507,13 @@ export class TextModel extends Disposable implements model.ITextModel {
}
counts = savedCounts.get(languageId)!;
};
const searchInRange = (modeBrackets: RichEditBrackets, lineNumber: number, lineText: string, searchStartOffset: number, searchEndOffset: number): [Range, Range] | null => {
let totalCallCount = 0;
const searchInRange = (modeBrackets: RichEditBrackets, lineNumber: number, lineText: string, searchStartOffset: number, searchEndOffset: number): [Range, Range] | null | BracketSearchCanceled => {
while (true) {
if (continueSearchPredicate && (++totalCallCount) % 100 === 0 && !continueSearchPredicate()) {
return BracketSearchCanceled.INSTANCE;
}
const r = BracketsUtils.findNextBracketInRange(modeBrackets.forwardRegex, lineNumber, lineText, searchStartOffset, searchEndOffset);
if (!r) {
break;
@@ -2485,7 +2529,7 @@ export class TextModel extends Disposable implements model.ITextModel {
}
if (counts[bracket.index] === -1) {
return this._matchFoundBracket(r, bracket, false);
return this._matchFoundBracket(r, bracket, false, continueSearchPredicate);
}
}
@@ -2496,12 +2540,7 @@ export class TextModel extends Disposable implements model.ITextModel {
let languageId: LanguageId = -1;
let modeBrackets: RichEditBrackets | null = null;
const startTime = Date.now();
for (let lineNumber = position.lineNumber; lineNumber <= lineCount; lineNumber++) {
const elapsedTime = Date.now() - startTime;
if (elapsedTime > maxDuration) {
return null;
}
const lineTokens = this._getLineTokens(lineNumber);
const tokenCount = lineTokens.getCount();
const lineText = this._buffer.getLineContent(lineNumber);
@@ -2530,7 +2569,7 @@ export class TextModel extends Disposable implements model.ITextModel {
if (modeBrackets && prevSearchInToken && searchStartOffset !== searchEndOffset) {
const r = searchInRange(modeBrackets, lineNumber, lineText, searchStartOffset, searchEndOffset);
if (r) {
return r;
return stripBracketSearchCanceled(r);
}
prevSearchInToken = false;
}
@@ -2555,7 +2594,7 @@ export class TextModel extends Disposable implements model.ITextModel {
if (modeBrackets && prevSearchInToken && searchStartOffset !== searchEndOffset) {
const r = searchInRange(modeBrackets, lineNumber, lineText, searchStartOffset, searchEndOffset);
if (r) {
return r;
return stripBracketSearchCanceled(r);
}
}
}
@@ -2566,7 +2605,7 @@ export class TextModel extends Disposable implements model.ITextModel {
if (modeBrackets && prevSearchInToken && searchStartOffset !== searchEndOffset) {
const r = searchInRange(modeBrackets, lineNumber, lineText, searchStartOffset, searchEndOffset);
if (r) {
return r;
return stripBracketSearchCanceled(r);
}
}
}
+6 -4
View File
@@ -1034,10 +1034,12 @@ export class TokensStore2 {
aIndex++;
}
if (aIndex < aLen && aTokens.getEndOffset(aIndex) === bEndCharacter) {
// `a` ends exactly at the same spot as `b`!
emitToken(aTokens.getEndOffset(aIndex), (aTokens.getMetadata(aIndex) & aMask) | (bMetadata & bMask));
aIndex++;
if (aIndex < aLen) {
emitToken(bEndCharacter, (aTokens.getMetadata(aIndex) & aMask) | (bMetadata & bMask));
if (aTokens.getEndOffset(aIndex) === bEndCharacter) {
// `a` ends exactly at the same spot as `b`!
aIndex++;
}
} else {
const aMergeIndex = Math.min(Math.max(0, aIndex - 1), aLen - 1);
+6 -4
View File
@@ -94,13 +94,15 @@ export function getWordAtText(column: number, wordDefinition: RegExp, text: stri
// should stop so that subsequent search don't repeat previous searches
const regexIndex = pos - config.windowSize * i;
wordDefinition.lastIndex = Math.max(0, regexIndex);
match = _findRegexMatchEnclosingPosition(wordDefinition, text, pos, prevRegexIndex);
const thisMatch = _findRegexMatchEnclosingPosition(wordDefinition, text, pos, prevRegexIndex);
// stop: found something
if (match) {
if (!thisMatch && match) {
// stop: we have something
break;
}
match = thisMatch;
// stop: searched at start
if (regexIndex <= 0) {
break;
@@ -112,7 +114,7 @@ export function getWordAtText(column: number, wordDefinition: RegExp, text: stri
let result = {
word: match[0],
startColumn: textOffset + 1 + match.index!,
endColumn: textOffset + 1 + wordDefinition.lastIndex
endColumn: textOffset + 1 + match.index! + match[0].length
};
wordDefinition.lastIndex = 0;
return result;
+15
View File
@@ -1531,6 +1531,21 @@ export interface CommentReaction {
readonly canEdit?: boolean;
}
/**
* @internal
*/
export interface CommentOptions {
/**
* An optional string to show on the comment input box when it's collapsed.
*/
prompt?: string;
/**
* An optional string to show as placeholder in the comment input box when it's focused.
*/
placeHolder?: string;
}
/**
* @internal
*/
+12 -1
View File
@@ -9,6 +9,7 @@ import { LanguageId, LanguageIdentifier } from 'vs/editor/common/modes';
import { LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageConfigurationRegistry';
import { ILanguageExtensionPoint } from 'vs/editor/common/services/modeService';
import { Registry } from 'vs/platform/registry/common/platform';
import { IDisposable } from 'vs/base/common/lifecycle';
// Define extension point ids
export const Extensions = {
@@ -30,9 +31,19 @@ export class EditorModesRegistry {
// --- languages
public registerLanguage(def: ILanguageExtensionPoint): void {
public registerLanguage(def: ILanguageExtensionPoint): IDisposable {
this._languages.push(def);
this._onDidChangeLanguages.fire(undefined);
return {
dispose: () => {
for (let i = 0, len = this._languages.length; i < len; i++) {
if (this._languages[i] === def) {
this._languages.splice(i, 1);
return;
}
}
}
};
}
public setDynamicLanguages(def: ILanguageExtensionPoint[]): void {
this._dynamicLanguages = def;
@@ -3,7 +3,6 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as nls from 'vs/nls';
import { Emitter, Event } from 'vs/base/common/event';
import { Disposable, IDisposable, DisposableStore, dispose } from 'vs/base/common/lifecycle';
import * as platform from 'vs/base/common/platform';
@@ -28,13 +27,9 @@ import { ILogService } from 'vs/platform/log/common/log';
import { IUndoRedoService, IUndoRedoElement, IPastFutureElements } from 'vs/platform/undoRedo/common/undoRedo';
import { StringSHA1 } from 'vs/base/common/hash';
import { SingleModelEditStackElement, MultiModelEditStackElement, EditStackElement } from 'vs/editor/common/model/editStack';
import { IDialogService } from 'vs/platform/dialogs/common/dialogs';
import { Schemas } from 'vs/base/common/network';
import Severity from 'vs/base/common/severity';
import { SemanticTokensProviderStyling, toMultilineTokens2 } from 'vs/editor/common/services/semanticTokensProviderStyling';
export const MAINTAIN_UNDO_REDO_STACK = true;
export interface IEditorSemanticHighlightingOptions {
enabled?: boolean;
}
@@ -143,6 +138,8 @@ function isEditStackElements(elements: IUndoRedoElement[]): elements is EditStac
class DisposedModelInfo {
constructor(
public readonly uri: URI,
public readonly time: number,
public readonly heapSize: number,
public readonly sha1: string,
public readonly versionId: number,
public readonly alternativeVersionId: number,
@@ -151,8 +148,6 @@ class DisposedModelInfo {
export class ModelServiceImpl extends Disposable implements IModelService {
private static _PROMPT_UNDO_REDO_SIZE_LIMIT = 10 * 1024 * 1024; // 10MB
public _serviceBrand: undefined;
private readonly _onModelAdded: Emitter<ITextModel> = this._register(new Emitter<ITextModel>());
@@ -171,6 +166,7 @@ export class ModelServiceImpl extends Disposable implements IModelService {
*/
private readonly _models: { [modelId: string]: ModelData; };
private readonly _disposedModels: Map<string, DisposedModelInfo>;
private _disposedModelsHeapSize: number;
private readonly _semanticStyling: SemanticStyling;
constructor(
@@ -179,12 +175,12 @@ export class ModelServiceImpl extends Disposable implements IModelService {
@IThemeService private readonly _themeService: IThemeService,
@ILogService private readonly _logService: ILogService,
@IUndoRedoService private readonly _undoRedoService: IUndoRedoService,
@IDialogService private readonly _dialogService: IDialogService,
) {
super();
this._modelCreationOptionsByLanguageAndResource = Object.create(null);
this._models = {};
this._disposedModels = new Map<string, DisposedModelInfo>();
this._disposedModelsHeapSize = 0;
this._semanticStyling = this._register(new SemanticStyling(this._themeService, this._logService));
this._register(this._configurationService.onDidChangeConfiguration(() => this._updateModelOptions()));
@@ -267,6 +263,14 @@ export class ModelServiceImpl extends Disposable implements IModelService {
return platform.OS === platform.OperatingSystem.Linux || platform.OS === platform.OperatingSystem.Macintosh ? '\n' : '\r\n';
}
private _getMaxMemoryForClosedFilesUndoStack(): number {
const result = this._configurationService.getValue<number>('files.maxMemoryForClosedFilesUndoStackMB');
if (typeof result === 'number') {
return result * 1024 * 1024;
}
return 20 * 1024 * 1024;
}
public getCreationOptions(language: string, resource: URI | undefined, isForSimpleWidget: boolean): ITextModelCreationOptions {
let creationOptions = this._modelCreationOptionsByLanguageAndResource[language + resource];
if (!creationOptions) {
@@ -328,13 +332,40 @@ export class ModelServiceImpl extends Disposable implements IModelService {
// --- begin IModelService
private _insertDisposedModel(disposedModelData: DisposedModelInfo): void {
this._disposedModels.set(MODEL_ID(disposedModelData.uri), disposedModelData);
this._disposedModelsHeapSize += disposedModelData.heapSize;
}
private _removeDisposedModel(resource: URI): DisposedModelInfo | undefined {
const disposedModelData = this._disposedModels.get(MODEL_ID(resource));
if (disposedModelData) {
this._disposedModelsHeapSize -= disposedModelData.heapSize;
}
this._disposedModels.delete(MODEL_ID(resource));
return disposedModelData;
}
private _ensureDisposedModelsHeapSize(maxModelsHeapSize: number): void {
if (this._disposedModelsHeapSize > maxModelsHeapSize) {
// we must remove some old undo stack elements to free up some memory
const disposedModels: DisposedModelInfo[] = [];
this._disposedModels.forEach(entry => disposedModels.push(entry));
disposedModels.sort((a, b) => a.time - b.time);
while (disposedModels.length > 0 && this._disposedModelsHeapSize > maxModelsHeapSize) {
const disposedModel = disposedModels.shift()!;
this._removeDisposedModel(disposedModel.uri);
this._undoRedoService.removeElements(disposedModel.uri);
}
}
}
private _createModelData(value: string | ITextBufferFactory, languageIdentifier: LanguageIdentifier, resource: URI | undefined, isForSimpleWidget: boolean): ModelData {
// create & save the model
const options = this.getCreationOptions(languageIdentifier.language, resource, isForSimpleWidget);
const model: TextModel = new TextModel(value, options, languageIdentifier, resource, this._undoRedoService);
if (resource && this._disposedModels.has(MODEL_ID(resource))) {
const disposedModelData = this._disposedModels.get(MODEL_ID(resource))!;
this._disposedModels.delete(MODEL_ID(resource));
const disposedModelData = this._removeDisposedModel(resource)!;
const elements = this._undoRedoService.getElements(resource);
if (computeModelSha1(model) === disposedModelData.sha1 && isEditStackPastFutureElements(elements)) {
for (const element of elements.past) {
@@ -473,7 +504,7 @@ export class ModelServiceImpl extends Disposable implements IModelService {
const model = modelData.model;
let maintainUndoRedoStack = false;
let heapSize = 0;
if (MAINTAIN_UNDO_REDO_STACK && (resource.scheme === Schemas.file || resource.scheme === Schemas.vscodeRemote || resource.scheme === Schemas.userData)) {
if (resource.scheme === Schemas.file || resource.scheme === Schemas.vscodeRemote || resource.scheme === Schemas.userData) {
const elements = this._undoRedoService.getElements(resource);
if ((elements.past.length > 0 || elements.future.length > 0) && isEditStackPastFutureElements(elements)) {
maintainUndoRedoStack = true;
@@ -490,37 +521,27 @@ export class ModelServiceImpl extends Disposable implements IModelService {
}
}
if (maintainUndoRedoStack) {
// We only invalidate the elements, but they remain in the undo-redo service.
this._undoRedoService.setElementsIsValid(resource, false);
this._disposedModels.set(MODEL_ID(resource), new DisposedModelInfo(resource, computeModelSha1(model), model.getVersionId(), model.getAlternativeVersionId()));
} else {
if (!maintainUndoRedoStack) {
this._undoRedoService.removeElements(resource);
modelData.model.dispose();
return;
}
const maxMemory = this._getMaxMemoryForClosedFilesUndoStack();
if (heapSize > maxMemory) {
// the undo stack for this file would never fit in the configured memory, so don't bother with it.
this._undoRedoService.removeElements(resource);
modelData.model.dispose();
return;
}
this._ensureDisposedModelsHeapSize(maxMemory - heapSize);
// We only invalidate the elements, but they remain in the undo-redo service.
this._undoRedoService.setElementsIsValid(resource, false);
this._insertDisposedModel(new DisposedModelInfo(resource, Date.now(), heapSize, computeModelSha1(model), model.getVersionId(), model.getAlternativeVersionId()));
modelData.model.dispose();
// After disposing the model, prompt and ask if we should keep the undo-redo stack
if (maintainUndoRedoStack && heapSize > ModelServiceImpl._PROMPT_UNDO_REDO_SIZE_LIMIT) {
const mbSize = (heapSize / 1024 / 1024).toFixed(1);
this._dialogService.show(
Severity.Info,
nls.localize('undoRedoConfirm', "Keep the undo-redo stack for {0} in memory ({1} MB)?", (resource.scheme === Schemas.file ? resource.fsPath : resource.path), mbSize),
[
nls.localize('nok', "Discard"),
nls.localize('ok', "Keep"),
],
{
cancelId: 2
}
).then((result) => {
const discard = (result.choice === 2 || result.choice === 0);
if (discard) {
this._disposedModels.delete(MODEL_ID(resource));
this._undoRedoService.removeElements(resource);
}
});
}
}
public getModels(): ITextModel[] {
@@ -36,6 +36,7 @@ export const editorBracketMatchBackground = registerColor('editorBracketMatch.ba
export const editorBracketMatchBorder = registerColor('editorBracketMatch.border', { dark: '#888', light: '#B9B9B9', hc: contrastBorder }, nls.localize('editorBracketMatchBorder', 'Color for matching brackets boxes'));
export const editorOverviewRulerBorder = registerColor('editorOverviewRuler.border', { dark: '#7f7f7f4d', light: '#7f7f7f4d', hc: '#7f7f7f4d' }, nls.localize('editorOverviewRulerBorder', 'Color of the overview ruler border.'));
export const editorOverviewRulerBackground = registerColor('editorOverviewRuler.background', null, nls.localize('editorOverviewRulerBackground', 'Background color of the editor overview ruler. Only used when the minimap is enabled and placed on the right side of the editor.'));
export const editorGutter = registerColor('editorGutter.background', { dark: editorBackground, light: editorBackground, hc: editorBackground }, nls.localize('editorGutter', 'Background color of the editor gutter. The gutter contains the glyph margins and the line numbers.'));
@@ -119,6 +119,8 @@ function createLineBreaksFromPreviousLineBreaks(classifier: WrappingCharacterCla
let breakingOffsets: number[] = arrPool1;
let breakingOffsetsVisibleColumn: number[] = arrPool2;
let breakingOffsetsCount: number = 0;
let lastBreakingOffset = 0;
let lastBreakingOffsetVisibleColumn = 0;
let breakingColumn = firstLineBreakColumn;
const prevLen = prevBreakingOffsets.length;
@@ -138,8 +140,12 @@ function createLineBreaksFromPreviousLineBreaks(classifier: WrappingCharacterCla
while (prevIndex < prevLen) {
// Allow for prevIndex to be -1 (for the case where we hit a tab when walking backwards from the first break)
const prevBreakOffset = prevIndex < 0 ? 0 : prevBreakingOffsets[prevIndex];
const prevBreakoffsetVisibleColumn = prevIndex < 0 ? 0 : prevBreakingOffsetsVisibleColumn[prevIndex];
let prevBreakOffset = prevIndex < 0 ? 0 : prevBreakingOffsets[prevIndex];
let prevBreakOffsetVisibleColumn = prevIndex < 0 ? 0 : prevBreakingOffsetsVisibleColumn[prevIndex];
if (lastBreakingOffset > prevBreakOffset) {
prevBreakOffset = lastBreakingOffset;
prevBreakOffsetVisibleColumn = lastBreakingOffsetVisibleColumn;
}
let breakOffset = 0;
let breakOffsetVisibleColumn = 0;
@@ -148,10 +154,10 @@ function createLineBreaksFromPreviousLineBreaks(classifier: WrappingCharacterCla
let forcedBreakOffsetVisibleColumn = 0;
// initially, we search as much as possible to the right (if it fits)
if (prevBreakoffsetVisibleColumn <= breakingColumn) {
let visibleColumn = prevBreakoffsetVisibleColumn;
let prevCharCode = lineText.charCodeAt(prevBreakOffset - 1);
let prevCharCodeClass = classifier.get(prevCharCode);
if (prevBreakOffsetVisibleColumn <= breakingColumn) {
let visibleColumn = prevBreakOffsetVisibleColumn;
let prevCharCode = prevBreakOffset === 0 ? CharCode.Null : lineText.charCodeAt(prevBreakOffset - 1);
let prevCharCodeClass = prevBreakOffset === 0 ? CharacterClass.NONE : classifier.get(prevCharCode);
let entireLineFits = true;
for (let i = prevBreakOffset; i < len; i++) {
const charStartOffset = i;
@@ -169,7 +175,7 @@ function createLineBreaksFromPreviousLineBreaks(classifier: WrappingCharacterCla
charWidth = computeCharWidth(charCode, visibleColumn, tabSize, columnsForFullWidthChar);
}
if (canBreak(prevCharCode, prevCharCodeClass, charCode, charCodeClass)) {
if (charStartOffset > lastBreakingOffset && canBreak(prevCharCode, prevCharCodeClass, charCode, charCodeClass)) {
breakOffset = charStartOffset;
breakOffsetVisibleColumn = visibleColumn;
}
@@ -179,8 +185,14 @@ function createLineBreaksFromPreviousLineBreaks(classifier: WrappingCharacterCla
// check if adding character at `i` will go over the breaking column
if (visibleColumn > breakingColumn) {
// We need to break at least before character at `i`:
forcedBreakOffset = charStartOffset;
forcedBreakOffsetVisibleColumn = visibleColumn - charWidth;
if (charStartOffset > lastBreakingOffset) {
forcedBreakOffset = charStartOffset;
forcedBreakOffsetVisibleColumn = visibleColumn - charWidth;
} else {
// we need to advance at least by one character
forcedBreakOffset = i + 1;
forcedBreakOffsetVisibleColumn = visibleColumn;
}
if (visibleColumn - breakOffsetVisibleColumn > wrappedLineBreakColumn) {
// Cannot break at `breakOffset` => reset it if it was set
@@ -198,7 +210,7 @@ function createLineBreaksFromPreviousLineBreaks(classifier: WrappingCharacterCla
if (entireLineFits) {
// there is no more need to break => stop the outer loop!
if (breakingOffsetsCount > 0) {
// Add last segment
// Add last segment, no need to assign to `lastBreakingOffset` and `lastBreakingOffsetVisibleColumn`
breakingOffsets[breakingOffsetsCount] = prevBreakingOffsets[prevBreakingOffsets.length - 1];
breakingOffsetsVisibleColumn[breakingOffsetsCount] = prevBreakingOffsetsVisibleColumn[prevBreakingOffsets.length - 1];
breakingOffsetsCount++;
@@ -209,11 +221,11 @@ function createLineBreaksFromPreviousLineBreaks(classifier: WrappingCharacterCla
if (breakOffset === 0) {
// must search left
let visibleColumn = prevBreakoffsetVisibleColumn;
let visibleColumn = prevBreakOffsetVisibleColumn;
let charCode = lineText.charCodeAt(prevBreakOffset);
let charCodeClass = classifier.get(charCode);
let hitATabCharacter = false;
for (let i = prevBreakOffset - 1; i >= 0; i--) {
for (let i = prevBreakOffset - 1; i >= lastBreakingOffset; i--) {
const charStartOffset = i + 1;
const prevCharCode = lineText.charCodeAt(i);
@@ -290,7 +302,9 @@ function createLineBreaksFromPreviousLineBreaks(classifier: WrappingCharacterCla
breakOffsetVisibleColumn = forcedBreakOffsetVisibleColumn;
}
lastBreakingOffset = breakOffset;
breakingOffsets[breakingOffsetsCount] = breakOffset;
lastBreakingOffsetVisibleColumn = breakOffsetVisibleColumn;
breakingOffsetsVisibleColumn[breakingOffsetsCount] = breakOffsetVisibleColumn;
breakingOffsetsCount++;
breakingColumn = breakOffsetVisibleColumn + wrappedLineBreakColumn;
@@ -1,8 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
.monaco-menu .monaco-action-bar.vertical .action-label.hover {
background-color: #EEE;
}
@@ -3,7 +3,6 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import 'vs/css!./clipboard';
import * as nls from 'vs/nls';
import * as browser from 'vs/base/browser/browser';
import { KeyCode, KeyMod } from 'vs/base/common/keyCodes';
@@ -39,7 +39,6 @@ function contextKeyForSupportedActions(kind: CodeActionKind) {
const argsSchema: IJSONSchema = {
type: 'object',
required: ['kind'],
defaultSnippets: [{ body: { kind: '' } }],
properties: {
'kind': {
@@ -153,12 +153,13 @@ export class LightBulbWidget extends Disposable implements IContentWidget {
return this.hide();
}
const { lineNumber, column } = atPosition;
const model = this._editor.getModel();
if (!model) {
return this.hide();
}
const { lineNumber, column } = model.validatePosition(atPosition);
const tabSize = model.getOptions().tabSize;
const fontInfo = options.get(EditorOption.fontInfo);
const lineContent = model.getLineContent(lineNumber);
+2 -2
View File
@@ -150,7 +150,7 @@ export async function formatDocumentRangeWithProvider(
let cts: CancellationTokenSource;
if (isCodeEditor(editorOrModel)) {
model = editorOrModel.getModel();
cts = new EditorStateCancellationTokenSource(editorOrModel, CodeEditorStateFlag.Value | CodeEditorStateFlag.Position, token);
cts = new EditorStateCancellationTokenSource(editorOrModel, CodeEditorStateFlag.Value | CodeEditorStateFlag.Position, undefined, token);
} else {
model = editorOrModel;
cts = new TextModelCancellationTokenSource(editorOrModel, token);
@@ -238,7 +238,7 @@ export async function formatDocumentWithProvider(
let cts: CancellationTokenSource;
if (isCodeEditor(editorOrModel)) {
model = editorOrModel.getModel();
cts = new EditorStateCancellationTokenSource(editorOrModel, CodeEditorStateFlag.Value | CodeEditorStateFlag.Position, token);
cts = new EditorStateCancellationTokenSource(editorOrModel, CodeEditorStateFlag.Value | CodeEditorStateFlag.Position, undefined, token);
} else {
model = editorOrModel;
cts = new TextModelCancellationTokenSource(editorOrModel, token);
+4 -4
View File
@@ -430,7 +430,7 @@ export class NextMarkerAction extends MarkerNavigationAction {
id: NextMarkerAction.ID,
label: NextMarkerAction.LABEL,
alias: 'Go to Next Problem (Error, Warning, Info)',
precondition: EditorContextKeys.writable,
precondition: undefined,
kbOpts: { kbExpr: EditorContextKeys.focus, primary: KeyMod.Alt | KeyCode.F8, weight: KeybindingWeight.EditorContrib }
});
}
@@ -444,7 +444,7 @@ class PrevMarkerAction extends MarkerNavigationAction {
id: PrevMarkerAction.ID,
label: PrevMarkerAction.LABEL,
alias: 'Go to Previous Problem (Error, Warning, Info)',
precondition: EditorContextKeys.writable,
precondition: undefined,
kbOpts: { kbExpr: EditorContextKeys.focus, primary: KeyMod.Shift | KeyMod.Alt | KeyCode.F8, weight: KeybindingWeight.EditorContrib }
});
}
@@ -456,7 +456,7 @@ class NextMarkerInFilesAction extends MarkerNavigationAction {
id: 'editor.action.marker.nextInFiles',
label: nls.localize('markerAction.nextInFiles.label', "Go to Next Problem in Files (Error, Warning, Info)"),
alias: 'Go to Next Problem in Files (Error, Warning, Info)',
precondition: EditorContextKeys.writable,
precondition: undefined,
kbOpts: {
kbExpr: EditorContextKeys.focus,
primary: KeyCode.F8,
@@ -472,7 +472,7 @@ class PrevMarkerInFilesAction extends MarkerNavigationAction {
id: 'editor.action.marker.prevInFiles',
label: nls.localize('markerAction.previousInFiles.label', "Go to Previous Problem in Files (Error, Warning, Info)"),
alias: 'Go to Previous Problem in Files (Error, Warning, Info)',
precondition: EditorContextKeys.writable,
precondition: undefined,
kbOpts: {
kbExpr: EditorContextKeys.focus,
primary: KeyMod.Shift | KeyCode.F8,
@@ -960,6 +960,25 @@ suite('Editor Contrib - Line Operations', () => {
model.dispose();
});
test('Indenting on empty line should move cursor', () => {
const model = createTextModel(
[
''
].join('\n')
);
withTestCodeEditor(null, { model: model, useTabStops: false }, (editor) => {
const indentLinesAction = new IndentLinesAction();
editor.setPosition(new Position(1, 1));
executeAction(indentLinesAction, editor);
assert.equal(model.getLineContent(1), ' ');
assert.deepEqual(editor.getSelection(), new Selection(1, 5, 1, 5));
});
model.dispose();
});
test('issue #62112: Delete line does not work properly when multiple cursors are on line', () => {
const TEXT = [
'a',
@@ -257,7 +257,7 @@ export abstract class AbstractGotoSymbolQuickAccessProvider extends AbstractEdit
// case we want to skip the container query altogether.
let skipContainerQuery = false;
if (symbolQuery !== query) {
[symbolScore, symbolMatches] = scoreFuzzy2(symbolLabel, { ...query, values: undefined /* disable multi-query support */ }, filterPos, symbolLabelIconOffset);
[symbolScore, symbolMatches] = scoreFuzzy2(symbolLabelWithIcon, { ...query, values: undefined /* disable multi-query support */ }, filterPos, symbolLabelIconOffset);
if (typeof symbolScore === 'number') {
skipContainerQuery = true; // since we consumed the query, skip any container matching
}
@@ -265,7 +265,7 @@ export abstract class AbstractGotoSymbolQuickAccessProvider extends AbstractEdit
// Otherwise: score on the symbol query and match on the container later
if (typeof symbolScore !== 'number') {
[symbolScore, symbolMatches] = scoreFuzzy2(symbolLabel, symbolQuery, filterPos, symbolLabelIconOffset);
[symbolScore, symbolMatches] = scoreFuzzy2(symbolLabelWithIcon, symbolQuery, filterPos, symbolLabelIconOffset);
if (typeof symbolScore !== 'number') {
continue;
}
+3 -1
View File
@@ -168,6 +168,8 @@ class RenameController implements IEditorContribution {
if (this._cts.token.isCancellationRequested) {
return undefined;
}
this._cts.dispose();
this._cts = new EditorStateCancellationTokenSource(this.editor, CodeEditorStateFlag.Position | CodeEditorStateFlag.Value, loc.range);
// do rename at location
let selection = this.editor.getSelection();
@@ -180,7 +182,7 @@ class RenameController implements IEditorContribution {
}
const supportPreview = this._bulkEditService.hasPreviewHandler() && this._configService.getValue<boolean>(this.editor.getModel().uri, 'editor.rename.enablePreview');
const inputFieldResult = await this._renameInputField.getValue().getInput(loc.range, loc.text, selectionStart, selectionEnd, supportPreview);
const inputFieldResult = await this._renameInputField.getValue().getInput(loc.range, loc.text, selectionStart, selectionEnd, supportPreview, this._cts.token);
// no result, only hint to focus the editor or not
if (typeof inputFieldResult === 'boolean') {
@@ -7,7 +7,7 @@ import 'vs/css!./renameInputField';
import { DisposableStore } from 'vs/base/common/lifecycle';
import { ContentWidgetPositionPreference, ICodeEditor, IContentWidget, IContentWidgetPosition } from 'vs/editor/browser/editorBrowser';
import { Position } from 'vs/editor/common/core/position';
import { IRange, Range } from 'vs/editor/common/core/range';
import { IRange } from 'vs/editor/common/core/range';
import { ScrollType } from 'vs/editor/common/editorCommon';
import { localize } from 'vs/nls';
import { IContextKey, IContextKeyService, RawContextKey } from 'vs/platform/contextkey/common/contextkey';
@@ -16,6 +16,7 @@ import { IColorTheme, IThemeService } from 'vs/platform/theme/common/themeServic
import { EditorOption } from 'vs/editor/common/config/editorOptions';
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
import { toggleClass } from 'vs/base/browser/dom';
import { CancellationToken } from 'vs/base/common/cancellation';
export const CONTEXT_RENAME_INPUT_VISIBLE = new RawContextKey<boolean>('renameInputVisible', false);
@@ -149,7 +150,7 @@ export class RenameInputField implements IContentWidget {
}
}
getInput(where: IRange, value: string, selectionStart: number, selectionEnd: number, supportPreview: boolean): Promise<RenameInputFieldResult | boolean> {
getInput(where: IRange, value: string, selectionStart: number, selectionEnd: number, supportPreview: boolean, token: CancellationToken): Promise<RenameInputFieldResult | boolean> {
toggleClass(this._domNode!, 'preview', supportPreview);
@@ -185,14 +186,7 @@ export class RenameInputField implements IContentWidget {
});
};
let onCursorChanged = () => {
const editorPosition = this._editor.getPosition();
if (!editorPosition || !Range.containsPosition(where, editorPosition)) {
this.cancelInput(true);
}
};
disposeOnDone.add(this._editor.onDidChangeCursorSelection(onCursorChanged));
token.onCancellationRequested(() => this.cancelInput(true));
disposeOnDone.add(this._editor.onDidBlurEditorWidget(() => this.cancelInput(false)));
this._show();
@@ -51,7 +51,7 @@ suite('SmartSelect', () => {
setup(() => {
const configurationService = new TestConfigurationService();
const dialogService = new TestDialogService();
modelService = new ModelServiceImpl(configurationService, new TestTextResourcePropertiesService(configurationService), new TestThemeService(), new NullLogService(), new UndoRedoService(dialogService, new TestNotificationService()), dialogService);
modelService = new ModelServiceImpl(configurationService, new TestTextResourcePropertiesService(configurationService), new TestThemeService(), new NullLogService(), new UndoRedoService(dialogService, new TestNotificationService()));
mode = new MockJSMode();
});
@@ -18,7 +18,7 @@ import { Range } from 'vs/editor/common/core/range';
import { Selection } from 'vs/editor/common/core/selection';
import { IEditorContribution } from 'vs/editor/common/editorCommon';
import { EditorContextKeys } from 'vs/editor/common/editorContextKeys';
import { IModelDeltaDecoration, ITextModel, OverviewRulerLane, TrackedRangeStickiness } from 'vs/editor/common/model';
import { IModelDeltaDecoration, ITextModel, OverviewRulerLane, TrackedRangeStickiness, IWordAtPosition } from 'vs/editor/common/model';
import { ModelDecorationOptions } from 'vs/editor/common/model/textModel';
import { DocumentHighlight, DocumentHighlightKind, DocumentHighlightProviderRegistry } from 'vs/editor/common/modes';
import { IContextKey, IContextKeyService, RawContextKey } from 'vs/platform/contextkey/common/contextkey';
@@ -27,6 +27,7 @@ import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegis
import { activeContrastBorder, editorSelectionHighlight, editorSelectionHighlightBorder, overviewRulerSelectionHighlightForeground, registerColor } from 'vs/platform/theme/common/colorRegistry';
import { registerThemingParticipant, themeColorFromId } from 'vs/platform/theme/common/themeService';
import { EditorOption } from 'vs/editor/common/config/editorOptions';
import { alert } from 'vs/base/browser/ui/aria/aria';
const editorWordHighlight = registerColor('editor.wordHighlightBackground', { dark: '#575757B8', light: '#57575740', hc: null }, nls.localize('wordHighlight', 'Background color of a symbol during read-access, like reading a variable. The color must not be opaque so as not to hide underlying decorations.'), true);
const editorWordHighlightStrong = registerColor('editor.wordHighlightStrongBackground', { dark: '#004972B8', light: '#0e639c40', hc: null }, nls.localize('wordHighlightStrong', 'Background color of a symbol during write-access, like writing to a variable. The color must not be opaque so as not to hide underlying decorations.'), true);
@@ -245,6 +246,11 @@ class WordHighlighter {
this._ignorePositionChangeEvent = true;
this.editor.setPosition(dest.getStartPosition());
this.editor.revealRangeInCenterIfOutsideViewport(dest);
const word = this._getWord();
if (word) {
const lineContent = this.editor.getModel().getLineContent(dest.startLineNumber);
alert(`${lineContent}, ${newIndex + 1} of ${highlights.length} for '${word.word}'`);
}
} finally {
this._ignorePositionChangeEvent = false;
}
@@ -259,6 +265,11 @@ class WordHighlighter {
this._ignorePositionChangeEvent = true;
this.editor.setPosition(dest.getStartPosition());
this.editor.revealRangeInCenterIfOutsideViewport(dest);
const word = this._getWord();
if (word) {
const lineContent = this.editor.getModel().getLineContent(dest.startLineNumber);
alert(`${lineContent}, ${newIndex + 1} of ${highlights.length} for '${word.word}'`);
}
} finally {
this._ignorePositionChangeEvent = false;
}
@@ -312,6 +323,17 @@ class WordHighlighter {
this._run();
}
private _getWord(): IWordAtPosition | null {
let editorSelection = this.editor.getSelection();
let lineNumber = editorSelection.startLineNumber;
let startColumn = editorSelection.startColumn;
return this.model.getWordAtPosition({
lineNumber: lineNumber,
column: startColumn
});
}
private _run(): void {
let editorSelection = this.editor.getSelection();
@@ -321,14 +343,10 @@ class WordHighlighter {
return;
}
let lineNumber = editorSelection.startLineNumber;
let startColumn = editorSelection.startColumn;
let endColumn = editorSelection.endColumn;
let word = this.model.getWordAtPosition({
lineNumber: lineNumber,
column: startColumn
});
const word = this._getWord();
// The selection must be inside a word or surround one word at most
if (!word || word.startColumn > startColumn || word.endColumn < endColumn) {
@@ -465,20 +483,20 @@ class WordHighlighterContribution extends Disposable implements IEditorContribut
return editor.getContribution<WordHighlighterContribution>(WordHighlighterContribution.ID);
}
private wordHighligher: WordHighlighter | null;
private wordHighlighter: WordHighlighter | null;
constructor(editor: ICodeEditor, @IContextKeyService contextKeyService: IContextKeyService) {
super();
this.wordHighligher = null;
this.wordHighlighter = null;
const createWordHighlighterIfPossible = () => {
if (editor.hasModel()) {
this.wordHighligher = new WordHighlighter(editor, contextKeyService);
this.wordHighlighter = new WordHighlighter(editor, contextKeyService);
}
};
this._register(editor.onDidChangeModel((e) => {
if (this.wordHighligher) {
this.wordHighligher.dispose();
this.wordHighligher = null;
if (this.wordHighlighter) {
this.wordHighlighter.dispose();
this.wordHighlighter = null;
}
createWordHighlighterIfPossible();
}));
@@ -486,34 +504,34 @@ class WordHighlighterContribution extends Disposable implements IEditorContribut
}
public saveViewState(): boolean {
if (this.wordHighligher && this.wordHighligher.hasDecorations()) {
if (this.wordHighlighter && this.wordHighlighter.hasDecorations()) {
return true;
}
return false;
}
public moveNext() {
if (this.wordHighligher) {
this.wordHighligher.moveNext();
if (this.wordHighlighter) {
this.wordHighlighter.moveNext();
}
}
public moveBack() {
if (this.wordHighligher) {
this.wordHighligher.moveBack();
if (this.wordHighlighter) {
this.wordHighlighter.moveBack();
}
}
public restoreViewState(state: boolean | undefined): void {
if (this.wordHighligher && state) {
this.wordHighligher.restore();
if (this.wordHighlighter && state) {
this.wordHighlighter.restore();
}
}
public dispose(): void {
if (this.wordHighligher) {
this.wordHighligher.dispose();
this.wordHighligher = null;
if (this.wordHighlighter) {
this.wordHighlighter.dispose();
this.wordHighlighter = null;
}
super.dispose();
}
+2
View File
@@ -41,6 +41,8 @@ if (typeof global.require !== 'undefined' && typeof global.require.config === 'f
ignoreDuplicateModules: [
'vscode-languageserver-types',
'vscode-languageserver-types/main',
'vscode-languageserver-textdocument',
'vscode-languageserver-textdocument/main',
'vscode-nls',
'vscode-nls/vscode-nls',
'jsonc-parser',
@@ -560,6 +560,14 @@ export class StandaloneTelemetryService implements ITelemetryService {
return this.publicLog(eventName, data as any);
}
public publicLogError(eventName: string, data?: any): Promise<void> {
return Promise.resolve(undefined);
}
publicLogError2<E extends ClassifiedEvent<T> = never, T extends GDPRClassification<T> = never>(eventName: string, data?: StrictPropertyCheck<T, E>) {
return this.publicLogError(eventName, data as any);
}
public getTelemetryInfo(): Promise<ITelemetryInfo> {
throw new Error(`Not available`);
}
@@ -157,7 +157,7 @@ export module StaticServices {
export const undoRedoService = define(IUndoRedoService, (o) => new UndoRedoService(dialogService.get(o), notificationService.get(o)));
export const modelService = define(IModelService, (o) => new ModelServiceImpl(configurationService.get(o), resourcePropertiesService.get(o), standaloneThemeService.get(o), logService.get(o), undoRedoService.get(o), dialogService.get(o)));
export const modelService = define(IModelService, (o) => new ModelServiceImpl(configurationService.get(o), resourcePropertiesService.get(o), standaloneThemeService.get(o), logService.get(o), undoRedoService.get(o)));
export const markerDecorationsService = define(IMarkerDecorationsService, (o) => new MarkerDecorationsService(modelService.get(o), markerService.get(o)));
@@ -740,6 +740,24 @@ export class MonarchTokenizer implements modes.ITokenizationSupport {
throw monarchCommon.createError(this._lexer, 'lexer rule has no well-defined action in rule: ' + this._safeRuleName(rule));
}
const computeNewStateForEmbeddedMode = (enteringEmbeddedMode: string) => {
// substitute language alias to known modes to support syntax highlighting
let enteringEmbeddedModeId = this._modeService.getModeIdForLanguageName(enteringEmbeddedMode);
if (enteringEmbeddedModeId) {
enteringEmbeddedMode = enteringEmbeddedModeId;
}
const embeddedModeData = this._getNestedEmbeddedModeData(enteringEmbeddedMode);
if (pos < lineLength) {
// there is content from the embedded mode on this line
const restOfLine = line.substr(pos);
return this._nestedTokenize(restOfLine, MonarchLineStateFactory.create(stack, embeddedModeData), offsetDelta + pos, tokensCollector);
} else {
return MonarchLineStateFactory.create(stack, embeddedModeData);
}
};
// is the result a group match?
if (Array.isArray(result)) {
if (groupMatching && groupMatching.groups.length > 0) {
@@ -780,6 +798,12 @@ export class MonarchTokenizer implements modes.ITokenizationSupport {
matched = ''; // better set the next state too..
matches = null;
result = '';
// Even though `@rematch` was specified, if `nextEmbedded` also specified,
// a state transition should occur.
if (enteringEmbeddedMode !== null) {
return computeNewStateForEmbeddedMode(enteringEmbeddedMode);
}
}
// check progress
@@ -810,21 +834,7 @@ export class MonarchTokenizer implements modes.ITokenizationSupport {
}
if (enteringEmbeddedMode !== null) {
// substitute language alias to known modes to support syntax highlighting
let enteringEmbeddedModeId = this._modeService.getModeIdForLanguageName(enteringEmbeddedMode);
if (enteringEmbeddedModeId) {
enteringEmbeddedMode = enteringEmbeddedModeId;
}
let embeddedModeData = this._getNestedEmbeddedModeData(enteringEmbeddedMode);
if (pos < lineLength) {
// there is content from the embedded mode on this line
let restOfLine = line.substr(pos);
return this._nestedTokenize(restOfLine, MonarchLineStateFactory.create(stack, embeddedModeData), offsetDelta + pos, tokensCollector);
} else {
return MonarchLineStateFactory.create(stack, embeddedModeData);
}
return computeNewStateForEmbeddedMode(enteringEmbeddedMode);
}
}
@@ -44,9 +44,9 @@ export interface IMonarchLanguage {
* shorthands: [reg,act] == { regex: reg, action: act}
* and : [reg,act,nxt] == { regex: reg, action: act{ next: nxt }}
*/
export type IShortMonarchLanguageRule1 = [RegExp, IMonarchLanguageAction];
export type IShortMonarchLanguageRule1 = [string | RegExp, IMonarchLanguageAction];
export type IShortMonarchLanguageRule2 = [RegExp, IMonarchLanguageAction, string];
export type IShortMonarchLanguageRule2 = [string | RegExp, IMonarchLanguageAction, string];
export interface IExpandedMonarchLanguageRule {
/**
@@ -135,4 +135,4 @@ export interface IMonarchLanguageBracket {
* token class
*/
token: string;
}
}
@@ -0,0 +1,106 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as assert from 'assert';
import { ModeServiceImpl } from 'vs/editor/common/services/modeServiceImpl';
import { IModeService } from 'vs/editor/common/services/modeService';
import { MonarchTokenizer } from 'vs/editor/standalone/common/monarch/monarchLexer';
import { compile } from 'vs/editor/standalone/common/monarch/monarchCompile';
import { Token } from 'vs/editor/common/core/token';
import { TokenizationRegistry } from 'vs/editor/common/modes';
import { IMonarchLanguage } from 'vs/editor/standalone/common/monarch/monarchTypes';
import { ModesRegistry } from 'vs/editor/common/modes/modesRegistry';
suite('Monarch', () => {
function createMonarchTokenizer(modeService: IModeService, languageId: string, language: IMonarchLanguage): MonarchTokenizer {
return new MonarchTokenizer(modeService, null!, languageId, compile(languageId, language));
}
test('Ensure @rematch and nextEmbedded can be used together in Monarch grammar', () => {
const modeService = new ModeServiceImpl();
const innerModeRegistration = ModesRegistry.registerLanguage({
id: 'sql'
});
const innerModeTokenizationRegistration = TokenizationRegistry.register('sql', createMonarchTokenizer(modeService, 'sql', {
tokenizer: {
root: [
[/./, 'token']
]
}
}));
const SQL_QUERY_START = '(SELECT|INSERT|UPDATE|DELETE|CREATE|REPLACE|ALTER|WITH)';
const tokenizer = createMonarchTokenizer(modeService, 'test1', {
tokenizer: {
root: [
[`(\"\"\")${SQL_QUERY_START}`, [{ 'token': 'string.quote', }, { token: '@rematch', next: '@endStringWithSQL', nextEmbedded: 'sql', },]],
[/(""")$/, [{ token: 'string.quote', next: '@maybeStringIsSQL', },]],
],
maybeStringIsSQL: [
[/(.*)/, {
cases: {
[`${SQL_QUERY_START}\\b.*`]: { token: '@rematch', next: '@endStringWithSQL', nextEmbedded: 'sql', },
'@default': { token: '@rematch', switchTo: '@endDblDocString', },
}
}],
],
endDblDocString: [
['[^\']+', 'string'],
['\\\\\'', 'string'],
['\'\'\'', 'string', '@popall'],
['\'', 'string']
],
endStringWithSQL: [[/"""/, { token: 'string.quote', next: '@popall', nextEmbedded: '@pop', },]],
}
});
const lines = [
`mysql_query("""SELECT * FROM table_name WHERE ds = '<DATEID>'""")`,
`mysql_query("""`,
`SELECT *`,
`FROM table_name`,
`WHERE ds = '<DATEID>'`,
`""")`,
];
const actualTokens: Token[][] = [];
let state = tokenizer.getInitialState();
for (const line of lines) {
const result = tokenizer.tokenize(line, state, 0);
actualTokens.push(result.tokens);
state = result.endState;
}
assert.deepEqual(actualTokens, [
[
{ 'offset': 0, 'type': 'source.test1', 'language': 'test1' },
{ 'offset': 12, 'type': 'string.quote.test1', 'language': 'test1' },
{ 'offset': 15, 'type': 'token.sql', 'language': 'sql' },
{ 'offset': 61, 'type': 'string.quote.test1', 'language': 'test1' },
{ 'offset': 64, 'type': 'source.test1', 'language': 'test1' }
],
[
{ 'offset': 0, 'type': 'source.test1', 'language': 'test1' },
{ 'offset': 12, 'type': 'string.quote.test1', 'language': 'test1' }
],
[
{ 'offset': 0, 'type': 'token.sql', 'language': 'sql' }
],
[
{ 'offset': 0, 'type': 'token.sql', 'language': 'sql' }
],
[
{ 'offset': 0, 'type': 'token.sql', 'language': 'sql' }
],
[
{ 'offset': 0, 'type': 'string.quote.test1', 'language': 'test1' },
{ 'offset': 3, 'type': 'source.test1', 'language': 'test1' }
]
]);
innerModeTokenizationRegistration.dispose();
innerModeRegistration.dispose();
});
});
@@ -1428,6 +1428,28 @@ suite('Editor Controller - Regression tests', () => {
model.dispose();
});
test('issue #95591: Unindenting moves cursor to beginning of line', () => {
let model = createTextModel(
[
' '
].join('\n')
);
withTestCodeEditor(null, {
model: model,
useTabStops: false
}, (editor, cursor) => {
moveTo(cursor, 1, 9, false);
assertCursor(cursor, new Selection(1, 9, 1, 9));
CoreEditingCommands.Outdent.runEditorCommand(null, editor, null);
assert.equal(model.getLineContent(1), ' ');
assertCursor(cursor, new Selection(1, 5, 1, 5));
});
model.dispose();
});
test('Bug #16657: [editor] Tab on empty line of zero indentation moves cursor to position (1,1)', () => {
let model = createTextModel(
[
@@ -8,7 +8,7 @@ import { MultilineTokens2, SparseEncodedTokens, TokensStore2 } from 'vs/editor/c
import { Range } from 'vs/editor/common/core/range';
import { TextModel } from 'vs/editor/common/model/textModel';
import { IIdentifiedSingleEditOperation } from 'vs/editor/common/model';
import { MetadataConsts, TokenMetadata } from 'vs/editor/common/modes';
import { MetadataConsts, TokenMetadata, FontStyle } from 'vs/editor/common/modes';
import { createTextModel } from 'vs/editor/test/common/editorTestUtils';
import { LineTokens } from 'vs/editor/common/core/lineTokens';
@@ -387,4 +387,50 @@ suite('TokensStore', () => {
const lineTokens = store.addSemanticTokens(36451, new LineTokens(new Uint32Array([60, 1]), ` if (flags & ModifierFlags.Ambient) {`));
assert.equal(lineTokens.getCount(), 7);
});
test('issue #95949: Identifiers are colored in bold when targetting keywords', () => {
function createTMMetadata(foreground: number, fontStyle: number, languageId: number): number {
return (
(languageId << MetadataConsts.LANGUAGEID_OFFSET)
| (fontStyle << MetadataConsts.FONT_STYLE_OFFSET)
| (foreground << MetadataConsts.FOREGROUND_OFFSET)
) >>> 0;
}
function toArr(lineTokens: LineTokens): number[] {
let r: number[] = [];
for (let i = 0; i < lineTokens.getCount(); i++) {
r.push(lineTokens.getEndOffset(i));
r.push(lineTokens.getMetadata(i));
}
return r;
}
const store = new TokensStore2();
store.set([
new MultilineTokens2(1, new SparseEncodedTokens(new Uint32Array([
0, 6, 11, (1 << MetadataConsts.FOREGROUND_OFFSET) | MetadataConsts.SEMANTIC_USE_FOREGROUND,
])))
], true);
const lineTokens = store.addSemanticTokens(1, new LineTokens(new Uint32Array([
5, createTMMetadata(5, FontStyle.Bold, 53),
14, createTMMetadata(1, FontStyle.None, 53),
17, createTMMetadata(6, FontStyle.None, 53),
18, createTMMetadata(1, FontStyle.None, 53),
]), `const hello = 123;`));
const actual = toArr(lineTokens);
assert.deepEqual(actual, [
5, createTMMetadata(5, FontStyle.Bold, 53),
6, createTMMetadata(1, FontStyle.None, 53),
11, createTMMetadata(1, FontStyle.None, 53),
14, createTMMetadata(1, FontStyle.None, 53),
17, createTMMetadata(6, FontStyle.None, 53),
18, createTMMetadata(1, FontStyle.None, 53)
]);
});
});
@@ -13,7 +13,7 @@ import { Selection } from 'vs/editor/common/core/selection';
import { createStringBuilder } from 'vs/editor/common/core/stringBuilder';
import { DefaultEndOfLine } from 'vs/editor/common/model';
import { createTextBuffer } from 'vs/editor/common/model/textModel';
import { ModelServiceImpl, MAINTAIN_UNDO_REDO_STACK } from 'vs/editor/common/services/modelServiceImpl';
import { ModelServiceImpl } from 'vs/editor/common/services/modelServiceImpl';
import { ITextResourcePropertiesService } from 'vs/editor/common/services/textResourceConfigurationService';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService';
@@ -35,7 +35,7 @@ suite('ModelService', () => {
configService.setUserConfiguration('files', { 'eol': '\r\n' }, URI.file(platform.isWindows ? 'c:\\myroot' : '/myroot'));
const dialogService = new TestDialogService();
modelService = new ModelServiceImpl(configService, new TestTextResourcePropertiesService(configService), new TestThemeService(), new NullLogService(), new UndoRedoService(dialogService, new TestNotificationService()), dialogService);
modelService = new ModelServiceImpl(configService, new TestTextResourcePropertiesService(configService), new TestThemeService(), new NullLogService(), new UndoRedoService(dialogService, new TestNotificationService()));
});
teardown(() => {
@@ -310,44 +310,42 @@ suite('ModelService', () => {
assertComputeEdits(file1, file2);
});
if (MAINTAIN_UNDO_REDO_STACK) {
test('maintains undo for same resource and same content', () => {
const resource = URI.parse('file://test.txt');
test('maintains undo for same resource and same content', () => {
const resource = URI.parse('file://test.txt');
// create a model
const model1 = modelService.createModel('text', null, resource);
// make an edit
model1.pushEditOperations(null, [{ range: new Range(1, 5, 1, 5), text: '1' }], () => [new Selection(1, 5, 1, 5)]);
assert.equal(model1.getValue(), 'text1');
// dispose it
modelService.destroyModel(resource);
// create a model
const model1 = modelService.createModel('text', null, resource);
// make an edit
model1.pushEditOperations(null, [{ range: new Range(1, 5, 1, 5), text: '1' }], () => [new Selection(1, 5, 1, 5)]);
assert.equal(model1.getValue(), 'text1');
// dispose it
modelService.destroyModel(resource);
// create a new model with the same content
const model2 = modelService.createModel('text1', null, resource);
// undo
model2.undo();
assert.equal(model2.getValue(), 'text');
});
// create a new model with the same content
const model2 = modelService.createModel('text1', null, resource);
// undo
model2.undo();
assert.equal(model2.getValue(), 'text');
});
test('maintains version id and alternative version id for same resource and same content', () => {
const resource = URI.parse('file://test.txt');
test('maintains version id and alternative version id for same resource and same content', () => {
const resource = URI.parse('file://test.txt');
// create a model
const model1 = modelService.createModel('text', null, resource);
// make an edit
model1.pushEditOperations(null, [{ range: new Range(1, 5, 1, 5), text: '1' }], () => [new Selection(1, 5, 1, 5)]);
assert.equal(model1.getValue(), 'text1');
const versionId = model1.getVersionId();
const alternativeVersionId = model1.getAlternativeVersionId();
// dispose it
modelService.destroyModel(resource);
// create a model
const model1 = modelService.createModel('text', null, resource);
// make an edit
model1.pushEditOperations(null, [{ range: new Range(1, 5, 1, 5), text: '1' }], () => [new Selection(1, 5, 1, 5)]);
assert.equal(model1.getValue(), 'text1');
const versionId = model1.getVersionId();
const alternativeVersionId = model1.getAlternativeVersionId();
// dispose it
modelService.destroyModel(resource);
// create a new model with the same content
const model2 = modelService.createModel('text1', null, resource);
assert.equal(model2.getVersionId(), versionId);
assert.equal(model2.getAlternativeVersionId(), alternativeVersionId);
});
}
// create a new model with the same content
const model2 = modelService.createModel('text1', null, resource);
assert.equal(model2.getVersionId(), versionId);
assert.equal(model2.getAlternativeVersionId(), alternativeVersionId);
});
test('does not maintain undo for same resource and different content', () => {
const resource = URI.parse('file://test.txt');
@@ -147,7 +147,7 @@ suite('Editor ViewModel - MonospaceLineBreaksComputer', () => {
test('MonospaceLineBreaksComputer incremental 1', () => {
let factory = new MonospaceLineBreaksComputerFactory(EditorOptions.wordWrapBreakBeforeCharacters.defaultValue, EditorOptions.wordWrapBreakAfterCharacters.defaultValue);
const factory = new MonospaceLineBreaksComputerFactory(EditorOptions.wordWrapBreakBeforeCharacters.defaultValue, EditorOptions.wordWrapBreakAfterCharacters.defaultValue);
assertIncrementalLineBreaks(
factory, 'just some text and more', 4,
@@ -203,6 +203,17 @@ suite('Editor ViewModel - MonospaceLineBreaksComputer', () => {
);
});
test('issue #95686: CRITICAL: loop forever on the monospaceLineBreaksComputer', () => {
const factory = new MonospaceLineBreaksComputerFactory(EditorOptions.wordWrapBreakBeforeCharacters.defaultValue, EditorOptions.wordWrapBreakAfterCharacters.defaultValue);
assertIncrementalLineBreaks(
factory,
' <tr dmx-class:table-danger="(alt <= 50)" dmx-class:table-warning="(alt <= 200)" dmx-class:table-primary="(alt <= 400)" dmx-class:table-info="(alt <= 800)" dmx-class:table-success="(alt >= 400)">',
4,
179, ' <tr dmx-class:table-danger="(alt <= 50)" dmx-class:table-warning="(alt <= 200)" dmx-class:table-primary="(alt <= 400)" dmx-class:table-info="(alt <= 800)" |dmx-class:table-success="(alt >= 400)">',
1, ' | | | | | |<|t|r| |d|m|x|-|c|l|a|s|s|:|t|a|b|l|e|-|d|a|n|g|e|r|=|"|(|a|l|t| |<|=| |5|0|)|"| |d|m|x|-|c|l|a|s|s|:|t|a|b|l|e|-|w|a|r|n|i|n|g|=|"|(|a|l|t| |<|=| |2|0|0|)|"| |d|m|x|-|c|l|a|s|s|:|t|a|b|l|e|-|p|r|i|m|a|r|y|=|"|(|a|l|t| |<|=| |4|0|0|)|"| |d|m|x|-|c|l|a|s|s|:|t|a|b|l|e|-|i|n|f|o|=|"|(|a|l|t| |<|=| |8|0|0|)|"| |d|m|x|-|c|l|a|s|s|:|t|a|b|l|e|-|s|u|c|c|e|s|s|=|"|(|a|l|t| |>|=| |4|0|0|)|"|>',
WrappingIndent.Same
);
});
test('MonospaceLineBreaksComputer - CJK and Kinsoku Shori', () => {
let factory = new MonospaceLineBreaksComputerFactory('(', '\t)');
+4
View File
@@ -1491,6 +1491,10 @@ var AMDLoader;
result.getStats = function () {
return _this.getLoaderEvents();
};
result.config = function (params, shouldOverwrite) {
if (shouldOverwrite === void 0) { shouldOverwrite = false; }
_this.configure(params, shouldOverwrite);
};
result.__$__nodeRequire = AMDLoader.global.nodeRequire;
return result;
};
+2 -2
View File
@@ -6302,9 +6302,9 @@ declare namespace monaco.languages {
* shorthands: [reg,act] == { regex: reg, action: act}
* and : [reg,act,nxt] == { regex: reg, action: act{ next: nxt }}
*/
export type IShortMonarchLanguageRule1 = [RegExp, IMonarchLanguageAction];
export type IShortMonarchLanguageRule1 = [string | RegExp, IMonarchLanguageAction];
export type IShortMonarchLanguageRule2 = [RegExp, IMonarchLanguageAction, string];
export type IShortMonarchLanguageRule2 = [string | RegExp, IMonarchLanguageAction, string];
export interface IExpandedMonarchLanguageRule {
/**
@@ -53,6 +53,7 @@ export class AuthenticationTokenService extends Disposable implements IAuthentic
}
sendTokenFailed(): void {
this.setToken(undefined);
this._onTokenFailed.fire();
}
}
@@ -328,6 +328,11 @@ class ConfigurationRegistry implements IConfigurationRegistry {
this.configurationProperties[key] = properties[key];
}
if (!properties[key].deprecationMessage && properties[key].markdownDeprecationMessage) {
// If not set, default deprecationMessage to the markdown source
properties[key].deprecationMessage = properties[key].markdownDeprecationMessage;
}
propertyKeys.push(key);
}
}
+14 -10
View File
@@ -36,6 +36,7 @@ export interface ParsedArgs {
log?: string;
logExtensionHostCommunication?: boolean;
'extensions-dir'?: string;
'extensions-download-dir'?: string;
'builtin-extensions-dir'?: string;
extensionDevelopmentPath?: string[]; // // undefined or array of 1 or more local paths or URIs
extensionTestsPath?: string; // either a local path or a URI
@@ -54,7 +55,7 @@ export interface ParsedArgs {
'locate-extension'?: string[]; // undefined or array of 1 or more
'enable-proposed-api'?: string[]; // undefined or array of 1 or more
'open-url'?: boolean;
'skip-getting-started'?: boolean;
'skip-release-notes'?: boolean;
'disable-restore-windows'?: boolean;
'disable-telemetry'?: boolean;
'export-default-configuration'?: string;
@@ -143,6 +144,7 @@ export const OPTIONS: OptionDescriptions<Required<ParsedArgs>> = {
'help': { type: 'boolean', cat: 'o', alias: 'h', description: localize('help', "Print usage.") },
'extensions-dir': { type: 'string', deprecates: 'extensionHomePath', cat: 'e', args: 'dir', description: localize('extensionHomePath', "Set the root path for extensions.") },
'extensions-download-dir': { type: 'string' },
'builtin-extensions-dir': { type: 'string' },
'list-extensions': { type: 'boolean', cat: 'e', description: localize('listExtensions', "List the installed extensions.") },
'show-versions': { type: 'boolean', cat: 'e', description: localize('showVersions', "Show versions of installed extensions, when using --list-extension.") },
@@ -179,7 +181,7 @@ export const OPTIONS: OptionDescriptions<Required<ParsedArgs>> = {
'install-source': { type: 'string' },
'driver': { type: 'string' },
'logExtensionHostCommunication': { type: 'boolean' },
'skip-getting-started': { type: 'boolean' },
'skip-release-notes': { type: 'boolean' },
'disable-restore-windows': { type: 'boolean' },
'disable-telemetry': { type: 'boolean' },
'disable-updates': { type: 'boolean' },
@@ -265,23 +267,25 @@ export function parseArgs<T>(args: string[], options: OptionDescriptions<T>, err
const parsedArgs = minimist(args, { string, boolean, alias });
const cleanedArgs: any = {};
const remainingArgs: any = parsedArgs;
// https://github.com/microsoft/vscode/issues/58177
cleanedArgs._ = parsedArgs._.filter(arg => arg.length > 0);
delete parsedArgs._;
delete remainingArgs._;
for (let optionId in options) {
const o = options[optionId];
if (o.alias) {
delete parsedArgs[o.alias];
delete remainingArgs[o.alias];
}
let val = parsedArgs[optionId];
if (o.deprecates && parsedArgs.hasOwnProperty(o.deprecates)) {
let val = remainingArgs[optionId];
if (o.deprecates && remainingArgs.hasOwnProperty(o.deprecates)) {
if (!val) {
val = parsedArgs[o.deprecates];
val = remainingArgs[o.deprecates];
}
delete parsedArgs[o.deprecates];
delete remainingArgs[o.deprecates];
}
if (typeof val !== 'undefined') {
@@ -297,10 +301,10 @@ export function parseArgs<T>(args: string[], options: OptionDescriptions<T>, err
}
cleanedArgs[optionId] = val;
}
delete parsedArgs[optionId];
delete remainingArgs[optionId];
}
for (let key in parsedArgs) {
for (let key in remainingArgs) {
errorReporter.onUnknownOption(key);
}
@@ -36,6 +36,7 @@ export interface INativeEnvironmentService extends IEnvironmentService {
installSourcePath: string;
extensionsPath?: string;
extensionsDownloadPath?: string;
builtinExtensionsPath: string;
globalStorageHome: string;
@@ -150,6 +151,10 @@ export class EnvironmentService implements INativeEnvironmentService {
}
}
get extensionsDownloadPath(): string | undefined {
return parsePathArg(this._args['extensions-download-dir'], process);
}
@memoize
get extensionsPath(): string {
const fromArgs = parsePathArg(this._args['extensions-dir'], process);
@@ -13,7 +13,6 @@ import { IRequestService, asJson, asText } from 'vs/platform/request/common/requ
import { IRequestOptions, IRequestContext, IHeaders } from 'vs/base/parts/request/common/request';
import { isEngineValid } from 'vs/platform/extensions/common/extensionValidator';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { generateUuid } from 'vs/base/common/uuid';
import { values } from 'vs/base/common/map';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; // {{SQL CARBON EDIT}}
import { CancellationToken } from 'vs/base/common/cancellation';
@@ -21,7 +20,6 @@ import { ILogService } from 'vs/platform/log/common/log';
import { IExtensionManifest, ExtensionsPolicy, ExtensionsPolicyKey } from 'vs/platform/extensions/common/extensions'; // {{SQL CARBON EDIT}} add imports
import { IFileService } from 'vs/platform/files/common/files';
import { URI } from 'vs/base/common/uri';
import { joinPath } from 'vs/base/common/resources';
import { IProductService } from 'vs/platform/product/common/productService';
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
import { find } from 'vs/base/common/arrays';
@@ -704,9 +702,8 @@ export class ExtensionGalleryService implements IExtensionGalleryService {
});
}
download(extension: IGalleryExtension, location: URI, operation: InstallOperation): Promise<URI> {
download(extension: IGalleryExtension, location: URI, operation: InstallOperation): Promise<void> {
this.logService.trace('ExtensionGalleryService#download', extension.identifier.id);
const zip = joinPath(location, generateUuid());
const data = getGalleryExtensionTelemetryData(extension);
const startTime = new Date().getTime();
/* __GDPR__
@@ -728,9 +725,8 @@ export class ExtensionGalleryService implements IExtensionGalleryService {
} : extension.assets.download;
return this.getAsset(downloadAsset)
.then(context => this.fileService.writeFile(zip, context.stream))
.then(() => log(new Date().getTime() - startTime))
.then(() => zip);
.then(context => this.fileService.writeFile(location, context.stream))
.then(() => log(new Date().getTime() - startTime));
}
getReadme(extension: IGalleryExtension, token: CancellationToken): Promise<string> {
@@ -155,7 +155,7 @@ export interface IExtensionGalleryService {
isEnabled(): boolean;
query(token: CancellationToken): Promise<IPager<IGalleryExtension>>;
query(options: IQueryOptions, token: CancellationToken): Promise<IPager<IGalleryExtension>>;
download(extension: IGalleryExtension, location: URI, operation: InstallOperation): Promise<URI>;
download(extension: IGalleryExtension, location: URI, operation: InstallOperation): Promise<void>;
reportStatistic(publisher: string, name: string, version: string, type: StatisticType): Promise<void>;
getReadme(extension: IGalleryExtension, token: CancellationToken): Promise<string>;
getManifest(extension: IGalleryExtension, token: CancellationToken): Promise<IExtensionManifest | null>;
@@ -0,0 +1,104 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { tmpdir } from 'os';
import { Disposable } from 'vs/base/common/lifecycle';
import { IFileService, IFileStatWithMetadata } from 'vs/platform/files/common/files';
import { IExtensionGalleryService, IGalleryExtension, InstallOperation } from 'vs/platform/extensionManagement/common/extensionManagement';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { URI } from 'vs/base/common/uri';
import { INativeEnvironmentService } from 'vs/platform/environment/node/environmentService';
import { joinPath } from 'vs/base/common/resources';
import { ExtensionIdentifierWithVersion, groupByExtension } from 'vs/platform/extensionManagement/common/extensionManagementUtil';
import { ILogService } from 'vs/platform/log/common/log';
import { generateUuid } from 'vs/base/common/uuid';
import * as semver from 'semver-umd';
const ExtensionIdVersionRegex = /^([^.]+\..+)-(\d+\.\d+\.\d+)$/;
export class ExtensionsDownloader extends Disposable {
private readonly extensionsDownloadDir: URI = URI.file(tmpdir());
private readonly cache: number = 0;
private readonly cleanUpPromise: Promise<void> = Promise.resolve();
constructor(
@IEnvironmentService environmentService: INativeEnvironmentService,
@IFileService private readonly fileService: IFileService,
@IExtensionGalleryService private readonly extensionGalleryService: IExtensionGalleryService,
@ILogService private readonly logService: ILogService,
) {
super();
if (environmentService.extensionsDownloadPath) {
this.extensionsDownloadDir = URI.file(environmentService.extensionsDownloadPath);
this.cache = 20; // Cache 20 downloads
this.cleanUpPromise = this.cleanUp();
}
}
async downloadExtension(extension: IGalleryExtension, operation: InstallOperation): Promise<URI> {
await this.cleanUpPromise;
const location = joinPath(this.extensionsDownloadDir, this.getName(extension));
await this.download(extension, location, operation);
return location;
}
async delete(location: URI): Promise<void> {
// Delete immediately if caching is disabled
if (!this.cache) {
await this.fileService.del(location);
}
}
private async download(extension: IGalleryExtension, location: URI, operation: InstallOperation): Promise<void> {
if (!await this.fileService.exists(location)) {
await this.extensionGalleryService.download(extension, location, operation);
}
}
private async cleanUp(): Promise<void> {
try {
if (!(await this.fileService.exists(this.extensionsDownloadDir))) {
this.logService.trace('Extension VSIX downlads cache dir does not exist');
return;
}
const folderStat = await this.fileService.resolve(this.extensionsDownloadDir, { resolveMetadata: true });
if (folderStat.children) {
const toDelete: URI[] = [];
const all: [ExtensionIdentifierWithVersion, IFileStatWithMetadata][] = [];
for (const stat of folderStat.children) {
const extension = this.parse(stat.name);
if (extension) {
all.push([extension, stat]);
}
}
const byExtension = groupByExtension(all, ([extension]) => extension.identifier);
const distinct: IFileStatWithMetadata[] = [];
for (const p of byExtension) {
p.sort((a, b) => semver.rcompare(a[0].version, b[0].version));
toDelete.push(...p.slice(1).map(e => e[1].resource)); // Delete outdated extensions
distinct.push(p[0][1]);
}
distinct.sort((a, b) => a.mtime - b.mtime); // sort by modified time
toDelete.push(...distinct.slice(0, Math.max(0, distinct.length - this.cache)).map(s => s.resource)); // Retain minimum cacheSize and delete the rest
await Promise.all(toDelete.map(resource => {
this.logService.trace('Deleting vsix from cache', resource.path);
return this.fileService.del(resource);
}));
}
} catch (e) {
this.logService.error(e);
}
}
private getName(extension: IGalleryExtension): string {
return this.cache ? new ExtensionIdentifierWithVersion(extension.identifier, extension.version).key().toLowerCase() : generateUuid();
}
private parse(name: string): ExtensionIdentifierWithVersion | null {
const matches = ExtensionIdVersionRegex.exec(name);
return matches && matches[1] && matches[2] ? new ExtensionIdentifierWithVersion({ id: matches[1] }, matches[2]) : null;
}
}
@@ -40,12 +40,13 @@ import { isEngineValid } from 'vs/platform/extensions/common/extensionValidator'
import { tmpdir } from 'os';
import { generateUuid } from 'vs/base/common/uuid';
import { IDownloadService } from 'vs/platform/download/common/download';
import { optional } from 'vs/platform/instantiation/common/instantiation';
import { optional, IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { Schemas } from 'vs/base/common/network';
import { CancellationToken } from 'vs/base/common/cancellation';
import { getPathFromAmdModule } from 'vs/base/common/amd';
import { getManifest } from 'vs/platform/extensionManagement/node/extensionManagementUtil';
import { IExtensionManifest, ExtensionType } from 'vs/platform/extensions/common/extensions';
import { ExtensionsDownloader } from 'vs/platform/extensionManagement/node/extensionDownloader';
const ERROR_SCANNING_SYS_EXTENSIONS = 'scanningSystem';
const ERROR_SCANNING_USER_EXTENSIONS = 'scanningUser';
@@ -113,6 +114,7 @@ export class ExtensionManagementService extends Disposable implements IExtension
private readonly installingExtensions: Map<string, CancelablePromise<ILocalExtension>> = new Map<string, CancelablePromise<ILocalExtension>>();
private readonly uninstallingExtensions: Map<string, CancelablePromise<void>> = new Map<string, CancelablePromise<void>>();
private readonly manifestCache: ExtensionsManifestCache;
private readonly extensionsDownloader: ExtensionsDownloader;
private readonly extensionLifecycle: ExtensionsLifecycle;
private readonly _onInstallExtension = this._register(new Emitter<InstallExtensionEvent>());
@@ -133,6 +135,7 @@ export class ExtensionManagementService extends Disposable implements IExtension
@ILogService private readonly logService: ILogService,
@optional(IDownloadService) private downloadService: IDownloadService,
@ITelemetryService private readonly telemetryService: ITelemetryService,
@IInstantiationService instantiationService: IInstantiationService,
) {
super();
this.systemExtensionsPath = environmentService.builtinExtensionsPath;
@@ -140,6 +143,7 @@ export class ExtensionManagementService extends Disposable implements IExtension
this.uninstalledPath = path.join(this.extensionsPath, '.obsolete');
this.uninstalledFileLimiter = new Queue();
this.manifestCache = this._register(new ExtensionsManifestCache(environmentService, this));
this.extensionsDownloader = this._register(instantiationService.createInstance(ExtensionsDownloader));
this.extensionLifecycle = this._register(new ExtensionsLifecycle(environmentService, this.logService));
this._register(toDisposable(() => {
@@ -347,7 +351,7 @@ export class ExtensionManagementService extends Disposable implements IExtension
this.downloadInstallableExtension(extension, operation)
.then(installableExtension => this.installExtension(installableExtension, ExtensionType.User, cancellationToken)
.then(local => pfs.rimraf(installableExtension.zipPath).finally(() => { }).then(() => local)))
.then(local => this.extensionsDownloader.delete(URI.file(installableExtension.zipPath)).finally(() => { }).then(() => local)))
.then(local => this.installDependenciesAndPackExtensions(local, existingExtension)
.then(() => local, error => this.uninstall(local, true).then(() => Promise.reject(error), () => Promise.reject(error))))
.then(
@@ -425,7 +429,7 @@ export class ExtensionManagementService extends Disposable implements IExtension
};
this.logService.trace('Started downloading extension:', extension.identifier.id);
return this.galleryService.download(extension, URI.file(tmpdir()), operation)
return this.extensionsDownloader.downloadExtension(extension, operation)
.then(
zip => {
const zipPath = zip.fsPath;
@@ -1003,6 +1007,6 @@ export class ExtensionManagementService extends Disposable implements IExtension
]
}
*/
this.telemetryService.publicLog(eventName, assign(extensionData, { success: !error, duration, errorcode }));
this.telemetryService.publicLogError(eventName, assign(extensionData, { success: !error, duration, errorcode }));
}
}
@@ -320,8 +320,10 @@ 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`.
*
* @param onCancel will be called if the user closed the notification without picking
* any of the provided choices.
* @param severity the severity of the notification. Either `Info`, `Warning` or `Error`.
* @param message the message to show as status.
* @param choices options to be choosen from.
* @param options provides some optional configuration options.
*
* @returns a handle on the notification to e.g. hide it or update message, buttons, etc.
*/
@@ -120,7 +120,7 @@ export default abstract class BaseErrorTelemetry {
private _flushBuffer(): void {
/*for (let error of this._buffer) { {{SQL CARBON EDIT}} don't log errors
type UnhandledErrorClassification = {} & ErrorEventFragment;
this._telemetryService.publicLog2<ErrorEvent, UnhandledErrorClassification>('UnhandledError', error, true);
this._telemetryService.publicLogError2<ErrorEvent, UnhandledErrorClassification>('UnhandledError', error);
}*/
this._buffer.length = 0;
}
@@ -33,6 +33,10 @@ export interface ITelemetryService {
publicLog2<E extends ClassifiedEvent<T> = never, T extends GDPRClassification<T> = never>(eventName: string, data?: StrictPropertyCheck<T, E>, anonymizeFilePaths?: boolean): Promise<void>;
publicLogError(errorEventName: string, data?: ITelemetryData): Promise<void>;
publicLogError2<E extends ClassifiedEvent<T> = never, T extends GDPRClassification<T> = never>(eventName: string, data?: StrictPropertyCheck<T, E>): Promise<void>;
setEnabled(value: boolean): void;
getTelemetryInfo(): Promise<ITelemetryInfo>;
@@ -17,6 +17,7 @@ import { ClassifiedEvent, StrictPropertyCheck, GDPRClassification } from 'vs/pla
export interface ITelemetryServiceConfig {
appender: ITelemetryAppender;
sendErrorTelemetry?: boolean;
commonProperties?: Promise<{ [name: string]: any }>;
piiPaths?: string[];
trueMachineId?: string;
@@ -34,6 +35,7 @@ export class TelemetryService implements ITelemetryService {
private _piiPaths: string[];
private _userOptIn: boolean;
private _enabled: boolean;
private _sendErrorTelemetry: boolean;
private readonly _disposables = new DisposableStore();
private _cleanupPatterns: RegExp[] = [];
@@ -47,6 +49,7 @@ export class TelemetryService implements ITelemetryService {
this._piiPaths = config.piiPaths || [];
this._userOptIn = true;
this._enabled = true;
this._sendErrorTelemetry = !!config.sendErrorTelemetry;
// static cleanup pattern for: `file:///DANGEROUS/PATH/resources/app/Useful/Information`
this._cleanupPatterns = [/file:\/\/\/.*?\/resources\/app\//gi];
@@ -144,6 +147,19 @@ export class TelemetryService implements ITelemetryService {
return this.publicLog(eventName, data as ITelemetryData, anonymizeFilePaths);
}
publicLogError(errorEventName: string, data?: ITelemetryData): Promise<any> {
if (!this._sendErrorTelemetry) {
return Promise.resolve(undefined);
}
// Send error event and anonymize paths
return this.publicLog(errorEventName, data, true);
}
publicLogError2<E extends ClassifiedEvent<T> = never, T extends GDPRClassification<T> = never>(eventName: string, data?: StrictPropertyCheck<T, E>): Promise<any> {
return this.publicLogError(eventName, data as ITelemetryData);
}
private _cleanupInfo(stack: string, anonymizeFilePaths?: boolean): string {
let updatedStack = stack;
@@ -19,6 +19,13 @@ export const NullTelemetryService = new class implements ITelemetryService {
publicLog2<E extends ClassifiedEvent<T> = never, T extends GDPRClassification<T> = never>(eventName: string, data?: StrictPropertyCheck<T, E>) {
return this.publicLog(eventName, data as ITelemetryData);
}
publicLogError(eventName: string, data?: ITelemetryData) {
return Promise.resolve(undefined);
}
publicLogError2<E extends ClassifiedEvent<T> = never, T extends GDPRClassification<T> = never>(eventName: string, data?: StrictPropertyCheck<T, E>) {
return this.publicLogError(eventName, data as ITelemetryData);
}
setEnabled() { }
isOptedIn = true;
getTelemetryInfo(): Promise<ITelemetryInfo> {
@@ -4,13 +4,14 @@
*--------------------------------------------------------------------------------------------*/
import * as assert from 'assert';
import { Emitter } from 'vs/base/common/event';
import { TelemetryService } from 'vs/platform/telemetry/common/telemetryService';
import { TelemetryService, ITelemetryServiceConfig } from 'vs/platform/telemetry/common/telemetryService';
import ErrorTelemetry from 'vs/platform/telemetry/browser/errorTelemetry';
import { NullAppender, ITelemetryAppender } from 'vs/platform/telemetry/common/telemetryUtils';
import * as Errors from 'vs/base/common/errors';
import * as sinon from 'sinon';
import { ITelemetryData } from 'vs/platform/telemetry/common/telemetry';
import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
class TestTelemetryAppender implements ITelemetryAppender {
@@ -208,6 +209,10 @@ suite('TelemetryService', () => {
private readonly promises: Promise<void>[] = [];
constructor(config: ITelemetryServiceConfig, configurationService: IConfigurationService) {
super({ ...config, sendErrorTelemetry: true }, configurationService);
}
join(): Promise<any> {
return Promise.all(this.promises);
}
@@ -343,6 +343,7 @@ export const diffInsertedOutline = registerColor('diffEditor.insertedTextBorder'
export const diffRemovedOutline = registerColor('diffEditor.removedTextBorder', { dark: null, light: null, hc: '#FF008F' }, nls.localize('diffEditorRemovedOutline', 'Outline color for text that got removed.'));
export const diffBorder = registerColor('diffEditor.border', { dark: null, light: null, hc: contrastBorder }, nls.localize('diffEditorBorder', 'Border color between the two text editors.'));
export const diffDiagonalFill = registerColor('diffEditor.diagonalFill', { dark: '#cccccc33', light: '#22222233', hc: null }, nls.localize('diffDiagonalFill', "Color of the diff editor's diagonal fill. The diagonal fill is used in side-by-side diff views."));
/**
* List and tree colors
+9 -3
View File
@@ -10,6 +10,7 @@ import { Event, Emitter } from 'vs/base/common/event';
import { localize } from 'vs/nls';
import { Extensions as JSONExtensions, IJSONContributionRegistry } from 'vs/platform/jsonschemas/common/jsonContributionRegistry';
import { RunOnceScheduler } from 'vs/base/common/async';
import * as Codicons from 'vs/base/common/codicons';
// ------ API types
@@ -96,7 +97,7 @@ class IconRegistry implements IIconRegistry {
public registerIcon(id: string, defaults: IconDefaults, description?: string, deprecationMessage?: string): ThemeIcon {
if (!description) {
description = localize('icon.defaultDescription', 'Icon with identifier {0}', id);
description = localize('icon.defaultDescription', 'Icon with identifier \'{0}\'', id);
}
let iconContribution: IconContribution = { id, description, defaults, deprecationMessage };
this.iconsById[id] = iconContribution;
@@ -163,8 +164,13 @@ export function getIconRegistry(): IIconRegistry {
return iconRegistry;
}
function initialize() {
for (const icon of Codicons.iconRegistry.all) {
registerIcon(icon.id, icon.definition);
}
Codicons.iconRegistry.onDidRegister(icon => registerIcon(icon.id, icon.definition));
}
initialize();
export const iconsSchemaId = 'vscode://schemas/icons';
@@ -24,7 +24,7 @@ export const typeAndModifierIdPattern = `^${idPattern}$`;
export const selectorPattern = `^(${idPattern}|\\*)(\\${CLASSIFIER_MODIFIER_SEPARATOR}${idPattern})*(\\${TOKEN_CLASSIFIER_LANGUAGE_SEPARATOR}${idPattern})?$`;
export const fontStylePattern = '^(\\s*(-?italic|-?bold|-?underline))*\\s*$';
export const fontStylePattern = '^(\\s*(italic|bold|underline))*\\s*$';
export interface TokenSelector {
match(type: string, modifiers: string[], language: string): number;
@@ -90,30 +90,24 @@ export namespace TokenStyle {
export function fromData(data: { foreground?: Color, bold?: boolean, underline?: boolean, italic?: boolean }): TokenStyle {
return new TokenStyle(data.foreground, data.bold, data.underline, data.italic);
}
export function fromSettings(foreground: string | undefined, fontStyle: string | undefined): TokenStyle {
export function fromSettings(foreground: string | undefined, fontStyle: string | undefined, bold?: boolean, underline?: boolean, italic?: boolean): TokenStyle {
let foregroundColor = undefined;
if (foreground !== undefined) {
foregroundColor = Color.fromHex(foreground);
}
let bold, underline, italic;
if (fontStyle !== undefined) {
fontStyle = fontStyle.trim();
if (fontStyle.length === 0) {
bold = italic = underline = false;
} else {
const expression = /-?italic|-?bold|-?underline/g;
let match;
while ((match = expression.exec(fontStyle))) {
switch (match[0]) {
case 'bold': bold = true; break;
case 'italic': italic = true; break;
case 'underline': underline = true; break;
}
bold = italic = underline = false;
const expression = /italic|bold|underline/g;
let match;
while ((match = expression.exec(fontStyle))) {
switch (match[0]) {
case 'bold': bold = true; break;
case 'italic': italic = true; break;
case 'underline': underline = true; break;
}
}
}
return new TokenStyle(foregroundColor, bold, underline, italic);
}
}
@@ -130,18 +124,18 @@ export interface TokenStyleDefaults {
hc?: TokenStyleValue;
}
export interface TokenStylingDefaultRule {
export interface SemanticTokenDefaultRule {
selector: TokenSelector;
defaults: TokenStyleDefaults;
}
export interface TokenStylingRule {
export interface SemanticTokenRule {
style: TokenStyle;
selector: TokenSelector;
}
export namespace TokenStylingRule {
export function fromJSONObject(registry: ITokenClassificationRegistry, o: any): TokenStylingRule | undefined {
export namespace SemanticTokenRule {
export function fromJSONObject(registry: ITokenClassificationRegistry, o: any): SemanticTokenRule | undefined {
if (o && typeof o._selector === 'string' && o._style) {
const style = TokenStyle.fromJSONObject(o._style);
if (style) {
@@ -153,13 +147,13 @@ export namespace TokenStylingRule {
}
return undefined;
}
export function toJSONObject(rule: TokenStylingRule): any {
export function toJSONObject(rule: SemanticTokenRule): any {
return {
_selector: rule.selector.id,
_style: TokenStyle.toJSONObject(rule.style)
};
}
export function equals(r1: TokenStylingRule | undefined, r2: TokenStylingRule | undefined) {
export function equals(r1: SemanticTokenRule | undefined, r2: SemanticTokenRule | undefined) {
if (r1 === r2) {
return true;
}
@@ -167,7 +161,7 @@ export namespace TokenStylingRule {
&& r1.selector && r2.selector && r1.selector.id === r2.selector.id
&& TokenStyle.equals(r1.style, r2.style);
}
export function is(r: any): r is TokenStylingRule {
export function is(r: any): r is SemanticTokenRule {
return r && r.selector && typeof r.selector.selectorString === 'string' && TokenStyle.is(r.style);
}
}
@@ -245,7 +239,7 @@ export interface ITokenClassificationRegistry {
/**
* The styling rules to used when a schema does not define any styling rules.
*/
getTokenStylingDefaultRules(): TokenStylingDefaultRule[];
getTokenStylingDefaultRules(): SemanticTokenDefaultRule[];
/**
* JSON schema for an object to assign styling to token classifications
@@ -264,14 +258,18 @@ class TokenClassificationRegistry implements ITokenClassificationRegistry {
private tokenTypeById: { [key: string]: TokenTypeOrModifierContribution };
private tokenModifierById: { [key: string]: TokenTypeOrModifierContribution };
private tokenStylingDefaultRules: TokenStylingDefaultRule[] = [];
private tokenStylingDefaultRules: SemanticTokenDefaultRule[] = [];
private typeHierarchy: { [id: string]: string[] };
private tokenStylingSchema: IJSONSchema & { properties: IJSONSchemaMap } = {
private tokenStylingSchema: IJSONSchema & { properties: IJSONSchemaMap, patternProperties: IJSONSchemaMap } = {
type: 'object',
properties: {},
additionalProperties: getStylingSchemeEntry(),
patternProperties: {
[selectorPattern]: getStylingSchemeEntry()
},
//errorMessage: nls.localize('schema.token.errors', 'Valid token selectors have the form (*|tokenType)(.tokenModifier)*(:tokenLanguage)?.'),
additionalProperties: false,
definitions: {
style: {
type: 'object',
@@ -289,11 +287,24 @@ class TokenClassificationRegistry implements ITokenClassificationRegistry {
},
fontStyle: {
type: 'string',
description: nls.localize('schema.token.fontStyle', 'Font style of the rule: \'italic\', \'bold\' or \'underline\' or a combination. The empty string unsets inherited settings.'),
description: nls.localize('schema.token.fontStyle', 'Sets the all font styles of the rule: \'italic\', \'bold\' or \'underline\' or a combination. All styles that are not listed are unset. The empty string unsets all styles.'),
pattern: fontStylePattern,
patternErrorMessage: nls.localize('schema.fontStyle.error', 'Font style must be \'italic\', \'bold\' or \'underline\' or a combination. The empty string unsets all styles.'),
defaultSnippets: [{ label: nls.localize('schema.token.fontStyle.none', 'None (clear inherited style)'), bodyText: '""' }, { body: 'italic' }, { body: 'bold' }, { body: 'underline' }, { body: 'italic underline' }, { body: 'bold underline' }, { body: 'italic bold underline' }]
},
bold: {
type: 'boolean',
description: nls.localize('schema.token.bold', 'Sets or unsets the font style to bold. Note, the presence of \'fontStyle\' overrides this setting.'),
},
italic: {
type: 'boolean',
description: nls.localize('schema.token.italic', 'Sets or unsets the font style to italic. Note, the presence of \'fontStyle\' overrides this setting.'),
},
underline: {
type: 'boolean',
description: nls.localize('schema.token.underline', 'Sets or unsets the font style to underline. Note, the presence of \'fontStyle\' overrides this setting.'),
}
},
defaultSnippets: [{ body: { foreground: '${1:#FF0000}', fontStyle: '${2:bold}' } }]
}
@@ -318,7 +329,8 @@ class TokenClassificationRegistry implements ITokenClassificationRegistry {
let tokenStyleContribution: TokenTypeOrModifierContribution = { num, id, superType, description, deprecationMessage };
this.tokenTypeById[id] = tokenStyleContribution;
this.tokenStylingSchema.properties[id] = getStylingSchemeEntry(description, deprecationMessage);
const stylingSchemeEntry = getStylingSchemeEntry(description, deprecationMessage);
this.tokenStylingSchema.properties[id] = stylingSchemeEntry;
this.typeHierarchy = {};
}
@@ -406,7 +418,7 @@ class TokenClassificationRegistry implements ITokenClassificationRegistry {
return this.tokenStylingSchema;
}
public getTokenStylingDefaultRules(): TokenStylingDefaultRule[] {
public getTokenStylingDefaultRules(): SemanticTokenDefaultRule[] {
return this.tokenStylingDefaultRules;
}
@@ -98,8 +98,13 @@ export function merge(originalLocalContent: string, originalRemoteContent: strin
return { conflictsSettings: [], localContent: updateIgnoredSettings(originalRemoteContent, originalLocalContent, ignoredSettings, formattingOptions), remoteContent: null, hasConflicts: false };
}
/* remote and local has changed */
/* local is empty and not synced before */
if (baseContent === null && isEmpty(originalLocalContent)) {
const localContent = areSame(originalLocalContent, originalRemoteContent, ignoredSettings) ? null : updateIgnoredSettings(originalRemoteContent, originalLocalContent, ignoredSettings, formattingOptions);
return { conflictsSettings: [], localContent, remoteContent: null, hasConflicts: false };
}
/* remote and local has changed */
let localContent = originalLocalContent;
let remoteContent = originalRemoteContent;
const local = parse(originalLocalContent);
@@ -258,6 +263,11 @@ export function areSame(localContent: string, remoteContent: string, ignoredSett
return true;
}
export function isEmpty(content: string): boolean {
const nodes = parseSettings(content);
return nodes.length === 0;
}
function compare(from: IStringDictionary<any> | null, to: IStringDictionary<any>, ignored: Set<string>): { added: Set<string>, removed: Set<string>, updated: Set<string> } {
const fromKeys = from ? Object.keys(from).filter(key => !ignored.has(key)) : [];
const toKeys = Object.keys(to).filter(key => !ignored.has(key));
@@ -6,15 +6,13 @@
import { IFileService, FileOperationError, FileOperationResult } from 'vs/platform/files/common/files';
import { UserDataSyncError, UserDataSyncErrorCode, SyncStatus, IUserDataSyncStoreService, IUserDataSyncLogService, IUserDataSyncUtilService, CONFIGURATION_SYNC_STORE_KEY, SyncResource, IUserDataSyncEnablementService, IUserDataSyncBackupStoreService, USER_DATA_SYNC_SCHEME, PREVIEW_DIR_NAME, ISyncResourceHandle } from 'vs/platform/userDataSync/common/userDataSync';
import { VSBuffer } from 'vs/base/common/buffer';
import { parse } from 'vs/base/common/json';
import { localize } from 'vs/nls';
import { Event } from 'vs/base/common/event';
import { createCancelablePromise } from 'vs/base/common/async';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { CancellationToken } from 'vs/base/common/cancellation';
import { updateIgnoredSettings, merge, getIgnoredSettings } from 'vs/platform/userDataSync/common/settingsMerge';
import { isEmptyObject } from 'vs/base/common/types';
import { updateIgnoredSettings, merge, getIgnoredSettings, isEmpty } from 'vs/platform/userDataSync/common/settingsMerge';
import { edit } from 'vs/platform/userDataSync/common/content';
import { IFileSyncPreviewResult, AbstractJsonFileSynchroniser, IRemoteUserData } from 'vs/platform/userDataSync/common/abstractSynchronizer';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
@@ -160,10 +158,7 @@ export class SettingsSynchroniser extends AbstractJsonFileSynchroniser {
if (localFileContent) {
const formatUtils = await this.getFormattingOptions();
const content = edit(localFileContent.value.toString(), [CONFIGURATION_SYNC_STORE_KEY], undefined, formatUtils);
const settings = parse(content);
if (!isEmptyObject(settings)) {
return true;
}
return !isEmpty(content);
}
} catch (error) {
if ((<FileOperationError>error).fileOperationResult !== FileOperationResult.FILE_NOT_FOUND) {
@@ -417,7 +417,7 @@ export class SnippetsSynchroniser extends AbstractSynchroniser implements IUserD
for (const entry of stat.children || []) {
const resource = entry.resource;
const extension = extname(resource);
if (extension === '.json' || extension === '.code-snippet') {
if (extension === '.json' || extension === '.code-snippets') {
const key = relativePath(this.snippetsFolder, resource)!;
const content = await this.fileService.readFile(resource);
snippets[key] = content;
@@ -42,7 +42,7 @@ export class UserDataAutoSyncService extends Disposable implements IUserDataAuto
}
private async updateEnablement(stopIfDisabled: boolean, auto: boolean): Promise<void> {
const enabled = await this.isAutoSyncEnabled();
const { enabled, reason } = await this.isAutoSyncEnabled();
if (this.enabled === enabled) {
return;
}
@@ -56,7 +56,7 @@ export class UserDataAutoSyncService extends Disposable implements IUserDataAuto
this.resetFailures();
if (stopIfDisabled) {
this.userDataSyncService.stop();
this.logService.info('Auto Sync: stopped.');
this.logService.info('Auto Sync: stopped because', reason);
}
}
@@ -95,10 +95,18 @@ export class UserDataAutoSyncService extends Disposable implements IUserDataAuto
}
}
private async isAutoSyncEnabled(): Promise<boolean> {
return this.userDataSyncEnablementService.isEnabled()
&& this.userDataSyncService.status !== SyncStatus.Uninitialized
&& !!(await this.authTokenService.getToken());
private async isAutoSyncEnabled(): Promise<{ enabled: boolean, reason?: string }> {
if (!this.userDataSyncEnablementService.isEnabled()) {
return { enabled: false, reason: 'sync is disabled' };
}
if (this.userDataSyncService.status === SyncStatus.Uninitialized) {
return { enabled: false, reason: 'sync is not initialized' };
}
const token = await this.authTokenService.getToken();
if (!token) {
return { enabled: false, reason: 'token is not avaialable' };
}
return { enabled: true };
}
private resetFailures(): void {
@@ -26,19 +26,6 @@ import { isArray, isString, isObject } from 'vs/base/common/types';
export const CONFIGURATION_SYNC_STORE_KEY = 'configurationSync.store';
export interface ISyncConfiguration {
sync: {
enable: boolean,
enableSettings: boolean,
enableKeybindings: boolean,
enableUIState: boolean,
enableExtensions: boolean,
keybindingsPerPlatform: boolean,
ignoredExtensions: string[],
ignoredSettings: string[]
}
}
export function getDisallowedIgnoredSettings(): string[] {
const allSettings = Registry.as<IConfigurationRegistry>(ConfigurationExtensions.Configuration).getConfigurationProperties();
return Object.keys(allSettings).filter(setting => !!allSettings[setting].disallowSyncIgnore);
@@ -723,6 +723,23 @@ suite('SettingsMerge - Merge', () => {
assert.deepEqual(actual.conflictsSettings, expectedConflicts);
assert.ok(actual.hasConflicts);
});
test('merge when remote has comments and local is empty', async () => {
const localContent = `
{
}`;
const remoteContent = stringify`
{
// this is a comment
"a": 1,
}`;
const actual = merge(localContent, remoteContent, null, [], [], formattingOptions);
assert.equal(actual.localContent, remoteContent);
assert.equal(actual.remoteContent, null);
assert.equal(actual.conflictsSettings.length, 0);
assert.ok(!actual.hasConflicts);
});
});
suite('SettingsMerge - Compute Remote Content', () => {
@@ -597,7 +597,7 @@ suite('SnippetsSync', () => {
});
test('sync global and language snippet', async () => {
await updateSnippet('global.code-snippet', globalSnippet, client2);
await updateSnippet('global.code-snippets', globalSnippet, client2);
await updateSnippet('html.json', htmlSnippet1, client2);
await client2.sync();
@@ -607,17 +607,17 @@ suite('SnippetsSync', () => {
const actual1 = await readSnippet('html.json', testClient);
assert.equal(actual1, htmlSnippet1);
const actual2 = await readSnippet('global.code-snippet', testClient);
const actual2 = await readSnippet('global.code-snippets', testClient);
assert.equal(actual2, globalSnippet);
const { content } = await testClient.read(testObject.resource);
assert.ok(content !== null);
const actual = parseSnippets(content!);
assert.deepEqual(actual, { 'html.json': htmlSnippet1, 'global.code-snippet': globalSnippet });
assert.deepEqual(actual, { 'html.json': htmlSnippet1, 'global.code-snippets': globalSnippet });
});
test('sync should ignore non snippets', async () => {
await updateSnippet('global.code-snippet', globalSnippet, client2);
await updateSnippet('global.code-snippets', globalSnippet, client2);
await updateSnippet('html.html', htmlSnippet1, client2);
await updateSnippet('typescript.json', tsSnippet1, client2);
await client2.sync();
@@ -628,7 +628,7 @@ suite('SnippetsSync', () => {
const actual1 = await readSnippet('typescript.json', testClient);
assert.equal(actual1, tsSnippet1);
const actual2 = await readSnippet('global.code-snippet', testClient);
const actual2 = await readSnippet('global.code-snippets', testClient);
assert.equal(actual2, globalSnippet);
const actual3 = await readSnippet('html.html', testClient);
assert.equal(actual3, null);
@@ -636,7 +636,7 @@ suite('SnippetsSync', () => {
const { content } = await testClient.read(testObject.resource);
assert.ok(content !== null);
const actual = parseSnippets(content!);
assert.deepEqual(actual, { 'typescript.json': tsSnippet1, 'global.code-snippet': globalSnippet });
assert.deepEqual(actual, { 'typescript.json': tsSnippet1, 'global.code-snippets': globalSnippet });
});
function parseSnippets(content: string): IStringDictionary<string> {
+1
View File
@@ -8173,6 +8173,7 @@ declare module 'vscode' {
* }
* };
* vscode.window.createTerminal({ name: 'Exit example', pty });
* ```
*/
onDidClose?: Event<void | number>;
+215 -140
View File
@@ -112,6 +112,9 @@ declare module 'vscode' {
* Get existing authentication sessions. Rejects if a provider with providerId is not
* registered, or if the user does not consent to sharing authentication information with
* the extension.
* @param providerId The id of the provider to use
* @param scopes A list of scopes representing the permissions requested. These are dependent on the authentication
* provider
*/
export function getSessions(providerId: string, scopes: string[]): Thenable<readonly AuthenticationSession[]>;
@@ -119,9 +122,20 @@ declare module 'vscode' {
* Prompt a user to login to create a new authenticaiton session. Rejects if a provider with
* providerId is not registered, or if the user does not consent to sharing authentication
* information with the extension.
* @param providerId The id of the provider to use
* @param scopes A list of scopes representing the permissions requested. These are dependent on the authentication
* provider
*/
export function login(providerId: string, scopes: string[]): Thenable<AuthenticationSession>;
/**
* Logout of a specific session.
* @param providerId The id of the provider to use
* @param sessionId The session id to remove
* provider
*/
export function logout(providerId: string, sessionId: string): Thenable<void>;
/**
* An [event](#Event) which fires when the array of sessions has changed, or data
* within a session has changed for a provider. Fires with the ids of the providers
@@ -720,17 +734,18 @@ declare module 'vscode' {
//#region debug: https://github.com/microsoft/vscode/issues/88230
/**
* VS Code can call the `provideDebugConfigurations` method of a `DebugConfigurationProvider` in two situations (aka 'scopes'):
* to provide the initial debug configurations for a newly created launch.json or to provide debug configurations dynamically based on context.
* A scope can be used when registering a `DebugConfigurationProvider` with #debug.registerDebugConfigurationProvider.
* A DebugConfigurationProviderTriggerKind specifies when the `provideDebugConfigurations` method of a `DebugConfigurationProvider` is triggered.
* Currently there are two situations: to provide the initial debug configurations for a newly created launch.json or
* to provide dynamically generated debug configurations when the user asks for them through the UI (e.g. via the "Select and Start Debugging" command).
* A trigger kind is used when registering a `DebugConfigurationProvider` with #debug.registerDebugConfigurationProvider.
*/
export enum DebugConfigurationProviderScope {
export enum DebugConfigurationProviderTriggerKind {
/**
* The 'initial' scope is used to ask for debug configurations to be copied into a newly created launch.json.
* `DebugConfigurationProvider.provideDebugConfigurations` is called to provide the initial debug configurations for a newly created launch.json.
*/
Initial = 1,
/**
* The 'dynamic' scope is used to ask for additional dynamic debug configurations to be presented to the user (in addition to the static configurations from the launch.json).
* `DebugConfigurationProvider.provideDebugConfigurations` is called to provide dynamically generated debug configurations when the user asks for them through the UI (e.g. via the "Select and Start Debugging" command).
*/
Dynamic = 2
}
@@ -738,19 +753,19 @@ declare module 'vscode' {
export namespace debug {
/**
* Register a [debug configuration provider](#DebugConfigurationProvider) for a specific debug type.
* The optional [scope](#DebugConfigurationProviderScope) argument can be used to bind the `provideDebugConfigurations` method of the provider to a specific context (aka scope).
* Currently two scopes are possible: with the value `Initial` (or if no scope argument is given) the `provideDebugConfigurations` method is used to find the initial debug configurations to be copied into a newly created launch.json.
* With a scope value `Dynamic` the `provideDebugConfigurations` method is used to dynamically determine debug configurations to be presented to the user in addition to the static configurations from the launch.json.
* Please note that the scope argument only applies to the `provideDebugConfigurations` method: so the `resolveDebugConfiguration` methods are not affected at all.
* Registering a single provider with resolve methods for different scopes, results in the same resolve methods called multiple times.
* The optional [triggerKind](#DebugConfigurationProviderTriggerKind) can be used to specify when the `provideDebugConfigurations` method of the provider is triggered.
* Currently two trigger kinds are possible: with the value `Initial` (or if no trigger kind argument is given) the `provideDebugConfigurations` method is used to provide the initial debug configurations to be copied into a newly created launch.json.
* With the trigger kind `Dynamic` the `provideDebugConfigurations` method is used to dynamically determine debug configurations to be presented to the user (in addition to the static configurations from the launch.json).
* Please note that the `triggerKind` argument only applies to the `provideDebugConfigurations` method: so the `resolveDebugConfiguration` methods are not affected at all.
* Registering a single provider with resolve methods for different trigger kinds, results in the same resolve methods called multiple times.
* More than one provider can be registered for the same type.
*
* @param type The debug type for which the provider is registered.
* @param provider The [debug configuration provider](#DebugConfigurationProvider) to register.
* @param scope The [scope](#DebugConfigurationProviderScope) for which the 'provideDebugConfiguration' method of the provider is registered.
* @param triggerKind The [trigger](#DebugConfigurationProviderTrigger) for which the 'provideDebugConfiguration' method of the provider is registered.
* @return A [disposable](#Disposable) that unregisters this provider when being disposed.
*/
export function registerDebugConfigurationProvider(debugType: string, provider: DebugConfigurationProvider, scope?: DebugConfigurationProviderScope): Disposable;
export function registerDebugConfigurationProvider(debugType: string, provider: DebugConfigurationProvider, triggerKind?: DebugConfigurationProviderTriggerKind): Disposable;
}
// deprecated debug API
@@ -983,6 +998,15 @@ declare module 'vscode' {
* A collection of mutations that an extension can apply to a process environment.
*/
export interface EnvironmentVariableCollection {
/**
* Whether the collection should be cached for the workspace and applied to the terminal
* across window reloads. When true the collection will be active immediately such when the
* window reloads. Additionally, this API will return the cached version if it exists. The
* collection will be invalidated when the extension is uninstalled or when the collection
* is cleared. Defaults to true.
*/
persistent: boolean;
/**
* Replace an environment variable with a value.
*
@@ -1026,26 +1050,14 @@ declare module 'vscode' {
* Clears all mutators from this collection.
*/
clear(): void;
/**
* Disposes the collection, if the collection was persisted it will no longer be retained
* across reloads.
*/
dispose(): void;
}
export namespace window {
export interface ExtensionContext {
/**
* Creates or returns the extension's environment variable collection for this workspace,
* enabling changes to be applied to terminal environment variables.
*
* @param persistent Whether the collection should be cached for the workspace and applied
* to the terminal across window reloads. When true the collection will be active
* immediately such when the window reloads. Additionally, this API will return the cached
* version if it exists. The collection will be invalidated when the extension is
* uninstalled or when the collection is disposed. Defaults to false.
* Gets the extension's environment variable collection for this workspace, enabling changes
* to be applied to terminal environment variables.
*/
export function getEnvironmentVariableCollection(persistent?: boolean): EnvironmentVariableCollection;
readonly environmentVariableCollection: EnvironmentVariableCollection;
}
//#endregion
@@ -1239,22 +1251,32 @@ declare module 'vscode' {
}
/**
* Event triggered by extensions to signal to VS Code that an edit has occurred on an [`EditableCustomDocument`](#EditableCustomDocument).
* Event triggered by extensions to signal to VS Code that an edit has occurred on an [`CustomDocument`](#CustomDocument).
*
* @see [`EditableCustomDocument.onDidChange`](#EditableCustomDocument.onDidChange).
* @see [`CustomDocumentProvider.onDidChangeCustomDocument`](#CustomDocumentProvider.onDidChangeCustomDocument).
*/
interface CustomDocumentEditEvent {
interface CustomDocumentEditEvent<T extends CustomDocument = CustomDocument> {
/**
* The document that the edit is for.
*/
readonly document: T;
/**
* Undo the edit operation.
*
* This is invoked by VS Code when the user triggers an undo.
* This is invoked by VS Code when the user undoes this edit. To implement `undo`, your
* extension should restore the document and editor to the state they were in just before this
* edit was added to VS Code's internal edit stack by `onDidChangeCustomDocument`.
*/
undo(): Thenable<void> | void;
/**
* Redo the edit operation.
*
* This is invoked by VS Code when the user triggers a redo.
* This is invoked by VS Code when the user redoes this edit. To implement `redo`, your
* extension should restore the document and editor to the state they were in just after this
* edit was added to VS Code's internal edit stack by `onDidChangeCustomDocument`.
*/
redo(): Thenable<void> | void;
@@ -1267,17 +1289,20 @@ declare module 'vscode' {
}
/**
* Event triggered by extensions to signal to VS Code that the content of a [`EditableCustomDocument`](#EditableCustomDocument)
* Event triggered by extensions to signal to VS Code that the content of a [`CustomDocument`](#CustomDocument)
* has changed.
*
* @see [`EditableCustomDocument.onDidChange`](#EditableCustomDocument.onDidChange).
* @see [`CustomDocumentProvider.onDidChangeCustomDocument`](#CustomDocumentProvider.onDidChangeCustomDocument).
*/
interface CustomDocumentContentChangeEvent {
// marker interface
interface CustomDocumentContentChangeEvent<T extends CustomDocument = CustomDocument> {
/**
* The document that the change is for.
*/
readonly document: T;
}
/**
* A backup for an [`EditableCustomDocument`](#EditableCustomDocument).
* A backup for an [`CustomDocument`](#CustomDocument).
*/
interface CustomDocumentBackup {
/**
@@ -1285,55 +1310,102 @@ declare module 'vscode' {
*
* This id is passed back to your extension in `openCustomDocument` when opening a custom editor from a backup.
*/
readonly backupId: string;
readonly id: string;
/**
* Dispose of the current backup.
* Delete the current backup.
*
* This is called by VS Code when it is clear the current backup, such as when a new backup is made or when the
* file is saved.
* This is called by VS Code when it is clear the current backup is no longer needed, such as when a new backup
* is made or when the file is saved.
*/
dispose(): void;
delete(): void;
}
/**
* Represents an editable custom document used by a [`CustomEditorProvider`](#CustomEditorProvider).
*
* `EditableCustomDocument` is how custom editors hook into standard VS Code operations such as save and undo. The
* document is also how custom editors notify VS Code that an edit has taken place.
* Additional information used to implement [`CustomEditableDocument.backup`](#CustomEditableDocument.backup).
*/
interface EditableCustomDocument extends CustomDocument {
interface CustomDocumentBackupContext {
/**
* Save the resource for a custom editor.
* Suggested file location to write the new backup.
*
* This method is invoked by VS Code when the user saves a custom editor. This can happen when the user
* triggers save while the custom editor is active, by commands such as `save all`, or by auto save if enabled.
* Note that your extension is free to ignore this and use its own strategy for backup.
*
* To implement `save`, the implementer must persist the custom editor. This usually means writing the
* file data for the custom document to disk. After `save` completes, any associated editor instances will
* no longer be marked as dirty.
*
* @param cancellation Token that signals the save is no longer required (for example, if another save was triggered).
*
* @return Thenable signaling that saving has completed.
* For editors for workspace resource, this destination will be in the workspace storage. The path may not
*/
save(cancellation: CancellationToken): Thenable<void>;
readonly destination: Uri;
}
/**
* Additional information about the opening custom document.
*/
interface CustomDocumentOpenContext {
/**
* The id of the backup to restore the document from or `undefined` if there is no backup.
*
* If this is provided, your extension should restore the editor from the backup instead of reading the file
* the user's workspace.
*/
readonly backupId?: string;
}
/**
* Provider for readonly custom editors that use a custom document model.
*
* Custom editors use [`CustomDocument`](#CustomDocument) as their document model instead of a [`TextDocument`](#TextDocument).
*
* You should use this type of custom editor when dealing with binary files or more complex scenarios. For simple
* text based documents, use [`CustomTextEditorProvider`](#CustomTextEditorProvider) instead.
*
* @param T Type of the custom document returned by this provider.
*/
export interface CustomReadonlyEditorProvider<T extends CustomDocument = CustomDocument> {
/**
* Save the resource for a custom editor to a different location.
* Create a new document for a given resource.
*
* This method is invoked by VS Code when the user triggers `save as` on a custom editor.
* `openCustomDocument` is called when the first editor for a given resource is opened, and the resolve document
* is passed to `resolveCustomEditor`. The resolved `CustomDocument` is re-used for subsequent editor opens.
* If all editors for a given resource are closed, the `CustomDocument` is disposed of. Opening an editor at
* this point will trigger another call to `openCustomDocument`.
*
* To implement `saveAs`, the implementer must persist the custom editor to `targetResource`. The
* existing editor will remain open after `saveAs` completes.
* @param uri Uri of the document to open.
* @param openContext Additional information about the opening custom document.
* @param token A cancellation token that indicates the result is no longer needed.
*
* @param targetResource Location to save to.
* @param cancellation Token that signals the save is no longer required.
*
* @return Thenable signaling that saving has completed.
* @return The custom document.
*/
saveAs(targetResource: Uri, cancellation: CancellationToken): Thenable<void>;
openCustomDocument(uri: Uri, openContext: CustomDocumentOpenContext, token: CancellationToken): Thenable<T> | T;
/**
* Resolve a custom editor for a given resource.
*
* This is called whenever the user opens a new editor for this `CustomEditorProvider`.
*
* To resolve a custom editor, the provider must fill in its initial html content and hook up all
* the event listeners it is interested it. The provider can also hold onto the `WebviewPanel` to use later,
* for example in a command. See [`WebviewPanel`](#WebviewPanel) for additional details.
*
* @param document Document for the resource being resolved.
* @param webviewPanel Webview to resolve.
* @param token A cancellation token that indicates the result is no longer needed.
*
* @return Optional thenable indicating that the custom editor has been resolved.
*/
resolveCustomEditor(document: T, webviewPanel: WebviewPanel, token: CancellationToken): Thenable<void> | void;
}
/**
* Provider for editiable custom editors that use a custom document model.
*
* Custom editors use [`CustomDocument`](#CustomDocument) as their document model instead of a [`TextDocument`](#TextDocument).
* This gives extensions full control over actions such as edit, save, and backup.
*
* You should use this type of custom editor when dealing with binary files or more complex scenarios. For simple
* text based documents, use [`CustomTextEditorProvider`](#CustomTextEditorProvider) instead.
*
* @param T Type of the custom document returned by this provider.
*/
export interface CustomEditorProvider<T extends CustomDocument = CustomDocument> extends CustomReadonlyEditorProvider<T> {
/**
* Signal that an edit has occurred inside a custom editor.
*
@@ -1354,10 +1426,43 @@ declare module 'vscode' {
*
* An editor should only ever fire `CustomDocumentEditEvent` events, or only ever fire `CustomDocumentContentChangeEvent` events.
*/
readonly onDidChange: Event<CustomDocumentEditEvent> | Event<CustomDocumentContentChangeEvent>;
readonly onDidChangeCustomDocument: Event<CustomDocumentEditEvent<T>> | Event<CustomDocumentContentChangeEvent<T>>;
/**
* Revert a custom editor to its last saved state.
* Save a custom document.
*
* This method is invoked by VS Code when the user saves a custom editor. This can happen when the user
* triggers save while the custom editor is active, by commands such as `save all`, or by auto save if enabled.
*
* To implement `save`, the implementer must persist the custom editor. This usually means writing the
* file data for the custom document to disk. After `save` completes, any associated editor instances will
* no longer be marked as dirty.
*
* @param document Document to save.
* @param cancellation Token that signals the save is no longer required (for example, if another save was triggered).
*
* @return Thenable signaling that saving has completed.
*/
saveCustomDocument(document: T, cancellation: CancellationToken): Thenable<void>;
/**
* Save a custom document to a different location.
*
* This method is invoked by VS Code when the user triggers 'save as' on a custom editor. The implementer must
* persist the custom editor to `destination`.
*
* When the user accepts save as, the current editor is be replaced by an non-dirty editor for the newly saved file.
*
* @param document Document to save.
* @param destination Location to save to.
* @param cancellation Token that signals the save is no longer required.
*
* @return Thenable signaling that saving has completed.
*/
saveCustomDocumentAs(document: T, destination: Uri, cancellation: CancellationToken): Thenable<void>;
/**
* Revert a custom document to its last saved state.
*
* This method is invoked by VS Code when the user triggers `File: Revert File` in a custom editor. (Note that
* this is only used using VS Code's `File: Revert File` command and not on a `git revert` of the file).
@@ -1366,17 +1471,15 @@ declare module 'vscode' {
* are displaying the document in the same state is saved in. This usually means reloading the file from the
* workspace.
*
* During `revert`, your extension should also clear any backups for the custom editor. Backups are only needed
* when there is a difference between an editor's state in VS Code and its save state on disk.
*
* @param document Document to revert.
* @param cancellation Token that signals the revert is no longer required.
*
* @return Thenable signaling that the change has completed.
*/
revert(cancellation: CancellationToken): Thenable<void>;
revertCustomDocument(document: T, cancellation: CancellationToken): Thenable<void>;
/**
* Back up the resource in its current state.
* Back up a dirty custom document.
*
* Backups are used for hot exit and to prevent data loss. Your `backup` method should persist the resource in
* its current state, i.e. with the edits applied. Most commonly this means saving the resource to disk in
@@ -1388,72 +1491,14 @@ declare module 'vscode' {
* made in quick succession, `backup` is only triggered after the last one. `backup` is not invoked when
* `auto save` is enabled (since auto save already persists resource ).
*
* @param document Document to backup.
* @param context Information that can be used to backup the document.
* @param cancellation Token that signals the current backup since a new backup is coming in. It is up to your
* extension to decided how to respond to cancellation. If for example your extension is backing up a large file
* in an operation that takes time to complete, your extension may decide to finish the ongoing backup rather
* than cancelling it to ensure that VS Code has some valid backup.
*/
backup(cancellation: CancellationToken): Thenable<CustomDocumentBackup>;
}
/**
* Additional information about the opening custom document.
*/
interface OpenCustomDocumentContext {
/**
* The id of the backup to restore the document from or `undefined` if there is no backup.
*
* If this is provided, your extension should restore the editor from the backup instead of reading the file
* the user's workspace.
*/
readonly backupId?: string;
}
/**
* Provider for custom editors that use a custom document model.
*
* Custom editors use [`CustomDocument`](#CustomDocument) as their document model instead of a [`TextDocument`](#TextDocument).
* This gives extensions full control over actions such as edit, save, and backup.
*
* You should use this type of custom editor when dealing with binary files or more complex scenarios. For simple
* text based documents, use [`CustomTextEditorProvider`](#CustomTextEditorProvider) instead.
*
* @param DocumentType Type of the custom document returned by this provider.
*/
export interface CustomEditorProvider<DocumentType extends CustomDocument = CustomDocument> {
/**
* Create a new document for a given resource.
*
* `openCustomDocument` is called when the first editor for a given resource is opened, and the resolve document
* is passed to `resolveCustomEditor`. The resolved `CustomDocument` is re-used for subsequent editor opens.
* If all editors for a given resource are closed, the `CustomDocument` is disposed of. Opening an editor at
* this point will trigger another call to `openCustomDocument`.
*
* @param uri Uri of the document to open.
* @param openContext Additional information about the opening custom document.
* @param token A cancellation token that indicates the result is no longer needed.
*
* @return The custom document.
*/
openCustomDocument(uri: Uri, openContext: OpenCustomDocumentContext, token: CancellationToken): Thenable<DocumentType> | DocumentType;
/**
* Resolve a custom editor for a given resource.
*
* This is called whenever the user opens a new editor for this `CustomEditorProvider`.
*
* To resolve a custom editor, the provider must fill in its initial html content and hook up all
* the event listeners it is interested it. The provider can also hold onto the `WebviewPanel` to use later,
* for example in a command. See [`WebviewPanel`](#WebviewPanel) for additional details.
*
* @param document Document for the resource being resolved.
* @param webviewPanel Webview to resolve.
* @param token A cancellation token that indicates the result is no longer needed.
*
* @return Optional thenable indicating that the custom editor has been resolved.
*/
resolveCustomEditor(document: DocumentType, webviewPanel: WebviewPanel, token: CancellationToken): Thenable<void> | void;
backupCustomDocument(document: T, context: CustomDocumentBackupContext, cancellation: CancellationToken): Thenable<CustomDocumentBackup>;
}
namespace window {
@@ -1462,11 +1507,13 @@ declare module 'vscode' {
*/
export function registerCustomEditorProvider2(
viewType: string,
provider: CustomEditorProvider,
provider: CustomReadonlyEditorProvider | CustomEditorProvider,
options?: {
readonly webviewOptions?: WebviewPanelOptions;
/**
* Only applies to `CustomReadonlyEditorProvider | CustomEditorProvider`.
*
* Indicates that the provider allows multiple editor instances to be open at the same time for
* the same resource.
*
@@ -1477,7 +1524,7 @@ declare module 'vscode' {
* When set, users can split and create copies of the custom editor. The custom editor must make sure it
* can properly synchronize the states of all editor instances for a resource so that they are consistent.
*/
readonly supportsMultipleEditorsPerResource?: boolean;
readonly supportsMultipleEditorsPerDocument?: boolean;
}
): Disposable;
}
@@ -1643,6 +1690,12 @@ declare module 'vscode' {
*/
editable?: boolean;
/**
* Controls whether the full notebook can be run at once.
* Defaults to true
*/
runnable?: boolean;
/**
* Default value for [cell editable metadata](#NotebookCellMetadata.editable).
* Defaults to true.
@@ -1759,11 +1812,12 @@ declare module 'vscode' {
export function registerNotebookOutputRenderer(type: string, outputSelector: NotebookOutputSelector, renderer: NotebookOutputRenderer): Disposable;
// remove activeNotebookDocument, now that there is activeNotebookEditor.document
export let activeNotebookDocument: NotebookDocument | undefined;
export let activeNotebookEditor: NotebookEditor | undefined;
// export const onDidChangeNotebookDocument: Event<NotebookDocumentChangeEvent>;
export const onDidChangeNotebookDocument: Event<NotebookDocumentChangeEvent>;
/**
* Create a document that is the concatenation of all notebook cells. By default all code-cells are included
@@ -2019,4 +2073,25 @@ declare module 'vscode' {
//#endregion
//#region Comment
export interface CommentOptions {
/**
* An optional string to show on the comment input box when it's collapsed.
*/
prompt?: string;
/**
* An optional string to show as placeholder in the comment input box when it's focused.
*/
placeHolder?: string;
}
export interface CommentController {
/**
* Comment controller options
*/
options?: CommentOptions;
}
//#endregion
}
@@ -16,6 +16,7 @@ import { MenuRegistry, MenuId } from 'vs/platform/actions/common/actions';
import { CommandsRegistry } from 'vs/platform/commands/common/commands';
import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput';
import { INotificationService } from 'vs/platform/notification/common/notification';
import { IStorageKeysSyncRegistryService } from 'vs/platform/userDataSync/common/storageKeys';
interface AllowedExtension {
id: string;
@@ -62,7 +63,8 @@ export class MainThreadAuthenticationProvider extends Disposable {
private readonly _proxy: ExtHostAuthenticationShape,
public readonly id: string,
public readonly displayName: string,
private readonly notificationService: INotificationService
private readonly notificationService: INotificationService,
private readonly storageKeysSyncRegistryService: IStorageKeysSyncRegistryService
) {
super();
}
@@ -148,6 +150,8 @@ export class MainThreadAuthenticationProvider extends Disposable {
order: 3
});
this.storageKeysSyncRegistryService.registerStorageKey({ key: `${this.id}-${session.account.displayName}`, version: 1 });
const manageCommand = CommandsRegistry.registerCommand({
id: `configureSessions${session.id}`,
handler: (accessor, args) => {
@@ -285,14 +289,15 @@ export class MainThreadAuthentication extends Disposable implements MainThreadAu
@IAuthenticationService private readonly authenticationService: IAuthenticationService,
@IDialogService private readonly dialogService: IDialogService,
@IStorageService private readonly storageService: IStorageService,
@INotificationService private readonly notificationService: INotificationService
@INotificationService private readonly notificationService: INotificationService,
@IStorageKeysSyncRegistryService private readonly storageKeysSyncRegistryService: IStorageKeysSyncRegistryService
) {
super();
this._proxy = extHostContext.getProxy(ExtHostContext.ExtHostAuthentication);
}
async $registerAuthenticationProvider(id: string, displayName: string): Promise<void> {
const provider = new MainThreadAuthenticationProvider(this._proxy, id, displayName, this.notificationService);
const provider = new MainThreadAuthenticationProvider(this._proxy, id, displayName, this.notificationService, this.storageKeysSyncRegistryService);
await provider.initialize();
this.authenticationService.registerAuthenticationProvider(id, provider);
}
@@ -316,14 +321,14 @@ export class MainThreadAuthentication extends Disposable implements MainThreadAu
const { choice } = await this.dialogService.show(
Severity.Info,
nls.localize('confirmAuthenticationAccess', "The extension '{0}' is trying to access authentication information for the {1} account '{2}'.", extensionName, providerName, accountName),
[nls.localize('cancel', "Cancel"), nls.localize('allow', "Allow")],
nls.localize('confirmAuthenticationAccess', "The extension '{0}' wants to access the {1} account '{2}'.", extensionName, providerName, accountName),
[nls.localize('allow', "Allow"), nls.localize('cancel', "Cancel")],
{
cancelId: 0
cancelId: 1
}
);
const allow = choice === 1;
const allow = choice === 0;
if (allow) {
allowList.push({ id: extensionId, name: extensionName });
this.storageService.store(`${providerId}-${accountName}`, JSON.stringify(allowList), StorageScope.GLOBAL);
@@ -336,13 +341,13 @@ export class MainThreadAuthentication extends Disposable implements MainThreadAu
const { choice } = await this.dialogService.show(
Severity.Info,
nls.localize('confirmLogin', "The extension '{0}' wants to sign in using {1}.", extensionName, providerName),
[nls.localize('cancel', "Cancel"), nls.localize('allow', "Allow")],
[nls.localize('allow', "Allow"), nls.localize('cancel', "Cancel")],
{
cancelId: 0
cancelId: 1
}
);
return choice === 1;
return choice === 0;
}
async $setTrustedExtension(providerId: string, accountName: string, extensionId: string, extensionName: string): Promise<void> {
@@ -177,6 +177,10 @@ export class MainThreadCommentController {
this._reactions = reactions;
}
get options() {
return this._features.options;
}
private readonly _threads: Map<number, MainThreadCommentThread> = new Map<number, MainThreadCommentThread>();
public activeCommentThread?: MainThreadCommentThread;
@@ -15,7 +15,7 @@ import severity from 'vs/base/common/severity';
import { AbstractDebugAdapter } from 'vs/workbench/contrib/debug/common/abstractDebugAdapter';
import { IWorkspaceFolder } from 'vs/platform/workspace/common/workspace';
import { convertToVSCPaths, convertToDAPaths } from 'vs/workbench/contrib/debug/common/debugUtils';
import { DebugConfigurationProviderScope } from 'vs/workbench/api/common/extHostTypes';
import { DebugConfigurationProviderTriggerKind } from 'vs/workbench/api/common/extHostTypes';
@extHostNamedCustomer(MainContext.MainThreadDebugService)
export class MainThreadDebugService implements MainThreadDebugServiceShape, IDebugAdapterFactory {
@@ -155,11 +155,11 @@ export class MainThreadDebugService implements MainThreadDebugServiceShape, IDeb
return Promise.resolve();
}
public $registerDebugConfigurationProvider(debugType: string, providerScope: DebugConfigurationProviderScope, hasProvide: boolean, hasResolve: boolean, hasResolve2: boolean, hasProvideDebugAdapter: boolean, handle: number): Promise<void> {
public $registerDebugConfigurationProvider(debugType: string, providerTriggerKind: DebugConfigurationProviderTriggerKind, hasProvide: boolean, hasResolve: boolean, hasResolve2: boolean, hasProvideDebugAdapter: boolean, handle: number): Promise<void> {
const provider = <IDebugConfigurationProvider>{
type: debugType,
scope: providerScope
triggerKind: providerTriggerKind
};
if (hasProvide) {
provider.provideDebugConfigurations = (folder, token) => {
@@ -24,7 +24,9 @@ import { ExtHostContext, ExtHostEditorsShape, IApplyEditsOptions, IExtHostContex
import { EditorViewColumn, editorGroupToViewColumn, viewColumnToEditorGroup } from 'vs/workbench/api/common/shared/editor';
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService';
import { DEFAULT_EDITOR_ID } from 'vs/workbench/contrib/files/common/files';
import { openEditorWith } from 'vs/workbench/contrib/files/common/openWith';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput';
export class MainThreadTextEditors implements MainThreadTextEditorsShape {
@@ -296,26 +298,17 @@ CommandsRegistry.registerCommand('_workbench.open', function (accessor: Services
CommandsRegistry.registerCommand('_workbench.openWith', (accessor: ServicesAccessor, args: [URI, string, ITextEditorOptions | undefined, EditorViewColumn | undefined]) => {
const editorService = accessor.get(IEditorService);
const editorGroupService = accessor.get(IEditorGroupsService);
const editorGroupsService = accessor.get(IEditorGroupsService);
const configurationService = accessor.get(IConfigurationService);
const quickInputService = accessor.get(IQuickInputService);
const [resource, id, options, position] = args;
const group = editorGroupService.getGroup(viewColumnToEditorGroup(editorGroupService, position)) ?? editorGroupService.activeGroup;
const group = editorGroupsService.getGroup(viewColumnToEditorGroup(editorGroupsService, position)) ?? editorGroupsService.activeGroup;
const textOptions = options ? { ...options, ignoreOverrides: true } : { ignoreOverrides: true };
const fileEditorInput = editorService.createEditorInput({ resource, forceFile: true });
if (id === DEFAULT_EDITOR_ID) {
return editorService.openEditor(fileEditorInput, textOptions, position);
}
const editors = editorService.getEditorOverrides(fileEditorInput, undefined, group);
for (const [handler, data] of editors) {
if (data.id === id) {
return handler.open(fileEditorInput, options, group, id);
}
}
return undefined;
const input = editorService.createEditorInput({ resource });
return openEditorWith(input, id, textOptions, group, editorService, configurationService, quickInputService);
});
@@ -8,12 +8,13 @@ import { MainContext, MainThreadNotebookShape, NotebookExtensionDescription, IEx
import { Disposable } from 'vs/base/common/lifecycle';
import { URI, UriComponents } from 'vs/base/common/uri';
import { INotebookService, IMainNotebookController } from 'vs/workbench/contrib/notebook/browser/notebookService';
import { INotebookTextModel, INotebookMimeTypeSelector, NOTEBOOK_DISPLAY_ORDER, NotebookCellOutputsSplice, CellKind, NotebookDocumentMetadata, NotebookCellMetadata, ICellEditOperation } from 'vs/workbench/contrib/notebook/common/notebookCommon';
import { INotebookTextModel, INotebookMimeTypeSelector, NOTEBOOK_DISPLAY_ORDER, NotebookCellOutputsSplice, CellKind, NotebookDocumentMetadata, NotebookCellMetadata, ICellEditOperation, ACCESSIBLE_NOTEBOOK_DISPLAY_ORDER } from 'vs/workbench/contrib/notebook/common/notebookCommon';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { NotebookTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookTextModel';
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
import { INotebookEditor } from 'vs/workbench/contrib/notebook/browser/notebookBrowser';
import { CancellationToken } from 'vs/base/common/cancellation';
import { IAccessibilityService } from 'vs/platform/accessibility/common/accessibility';
export class MainThreadNotebookDocument extends Disposable {
private _textModel: NotebookTextModel;
@@ -63,6 +64,7 @@ export class MainThreadNotebooks extends Disposable implements MainThreadNoteboo
@INotebookService private _notebookService: INotebookService,
@IConfigurationService private readonly configurationService: IConfigurationService,
@IEditorService private readonly editorService: IEditorService,
@IAccessibilityService private readonly accessibilityService: IAccessibilityService
) {
super();
@@ -85,22 +87,25 @@ export class MainThreadNotebooks extends Disposable implements MainThreadNoteboo
this._proxy.$updateActiveEditor(e.viewType, e.uri);
}));
let userOrder = this.configurationService.getValue<string[]>('notebook.displayOrder');
this._proxy.$acceptDisplayOrder({
defaultOrder: NOTEBOOK_DISPLAY_ORDER,
userOrder: userOrder
});
const updateOrder = () => {
let userOrder = this.configurationService.getValue<string[]>('notebook.displayOrder');
this._proxy.$acceptDisplayOrder({
defaultOrder: this.accessibilityService.isScreenReaderOptimized() ? ACCESSIBLE_NOTEBOOK_DISPLAY_ORDER : NOTEBOOK_DISPLAY_ORDER,
userOrder: userOrder
});
};
this.configurationService.onDidChangeConfiguration(e => {
updateOrder();
this._register(this.configurationService.onDidChangeConfiguration(e => {
if (e.affectedKeys.indexOf('notebook.displayOrder') >= 0) {
let userOrder = this.configurationService.getValue<string[]>('notebook.displayOrder');
this._proxy.$acceptDisplayOrder({
defaultOrder: NOTEBOOK_DISPLAY_ORDER,
userOrder: userOrder
});
updateOrder();
}
});
}));
this._register(this.accessibilityService.onDidChangeScreenReaderOptimized(() => {
updateOrder();
}));
}
async $registerNotebookRenderer(extension: NotebookExtensionDescription, type: string, selectors: INotebookMimeTypeSelector, handle: number, preloads: UriComponents[]): Promise<void> {
@@ -309,8 +309,8 @@ export class MainThreadWebviews extends Disposable implements extHostProtocol.Ma
this.registerEditorProvider(ModelType.Text, extensionData, viewType, options, capabilities, true);
}
public $registerCustomEditorProvider(extensionData: extHostProtocol.WebviewExtensionDescription, viewType: string, options: modes.IWebviewPanelOptions, supportsMultipleEditorsPerResource: boolean): void {
this.registerEditorProvider(ModelType.Custom, extensionData, viewType, options, {}, supportsMultipleEditorsPerResource);
public $registerCustomEditorProvider(extensionData: extHostProtocol.WebviewExtensionDescription, viewType: string, options: modes.IWebviewPanelOptions, supportsMultipleEditorsPerDocument: boolean): void {
this.registerEditorProvider(ModelType.Custom, extensionData, viewType, options, {}, supportsMultipleEditorsPerDocument);
}
private registerEditorProvider(
@@ -319,14 +319,14 @@ export class MainThreadWebviews extends Disposable implements extHostProtocol.Ma
viewType: string,
options: modes.IWebviewPanelOptions,
capabilities: extHostProtocol.CustomTextEditorCapabilities,
supportsMultipleEditorsPerResource: boolean,
supportsMultipleEditorsPerDocument: boolean,
): DisposableStore {
if (this._editorProviders.has(viewType)) {
throw new Error(`Provider for ${viewType} already registered`);
}
this._customEditorService.registerCustomEditorCapabilities(viewType, {
supportsMultipleEditorsPerResource
supportsMultipleEditorsPerDocument
});
const extension = reviveWebviewExtension(extensionData);
@@ -610,8 +610,9 @@ namespace HotExitState {
class MainThreadCustomEditorModel extends Disposable implements ICustomEditorModel, IWorkingCopy {
private _hotExitState: HotExitState.State = HotExitState.Allowed;
private readonly _fromBackup: boolean = false;
private _hotExitState: HotExitState.State = HotExitState.Allowed;
private _backupId: string | undefined;
private _currentEditIndex: number = -1;
private _savePoint: number = -1;
@@ -716,6 +717,10 @@ class MainThreadCustomEditorModel extends Disposable implements ICustomEditorMod
return this._viewType;
}
public get backupId() {
return this._backupId;
}
public pushEdit(editId: number, label: string | undefined) {
if (!this._editable) {
throw new Error('Document is not editable');
@@ -902,6 +907,7 @@ class MainThreadCustomEditorModel extends Disposable implements ICustomEditorMod
if (this._hotExitState === pendingState) {
this._hotExitState = HotExitState.Allowed;
backupData.meta!.backupId = backupId;
this._backupId = backupId;
}
} catch (e) {
// Make sure state has not changed in the meantime
@@ -1,3 +0,0 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M20.8394 20.337L14.9999 9.009V3.009H16.4999V1.509H14.9909V1.5L14.3069 1.5075H7.4999V3H8.9999V8.928L3.1589 20.3415C3.04535 20.57 2.99197 20.8237 3.00381 21.0786C3.01566 21.3335 3.09234 21.5812 3.22659 21.7982C3.36085 22.0152 3.54825 22.1944 3.77106 22.3187C3.99387 22.4431 4.24473 22.5086 4.4999 22.509H19.4999C19.7556 22.5087 20.0069 22.4431 20.2301 22.3184C20.4533 22.1937 20.6409 22.014 20.7751 21.7964C20.9093 21.5788 20.9856 21.3305 20.9969 21.075C21.0082 20.8196 20.9539 20.5656 20.8394 20.337ZM10.3394 9.612L10.4999 9.2895V3.054L13.4999 3.018V9.0105V9.3735L13.6664 9.696L16.4054 15.009H7.5734L10.3394 9.612ZM4.4999 21.0255L6.8114 16.509H17.1839L19.5044 21.009L4.4999 21.0255Z" fill="white"/>
</svg>

Before

Width:  |  Height:  |  Size: 810 B

@@ -30,6 +30,7 @@ import { IWorkbenchActionRegistry, Extensions as ActionExtensions } from 'vs/wor
import { SyncActionDescriptor } from 'vs/platform/actions/common/actions';
import { ViewPaneContainer } from 'vs/workbench/browser/parts/views/viewPaneContainer';
import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors';
import { Codicon } from 'vs/base/common/codicons';
export interface IUserFriendlyViewsContainerDescriptor {
id: string;
@@ -53,7 +54,8 @@ const viewsContainerSchema: IJSONSchema = {
description: localize('vscode.extension.contributes.views.containers.icon', "Path to the container icon. Icons are 24x24 centered on a 50x40 block and have a fill color of 'rgb(215, 218, 224)' or '#d7dae0'. It is recommended that icons be in SVG, though any image file type is accepted."),
type: 'string'
}
}
},
required: ['id', 'title', 'icon']
};
export const viewsContainersContribution: IJSONSchema = {
@@ -255,7 +257,7 @@ class ViewsExtensionHandler implements IWorkbenchContribution {
private registerTestViewContainer(): void {
const title = localize('test', "Test");
const icon = URI.parse(require.toUrl('./media/test.svg'));
const icon = Codicon.beaker.classNames;
this.registerCustomViewContainer(TEST_VIEW_CONTAINER_ID, title, icon, TEST_VIEW_CONTAINER_ORDER, undefined, ViewContainerLocation.Sidebar);
}
@@ -310,7 +312,7 @@ class ViewsExtensionHandler implements IWorkbenchContribution {
return order;
}
private registerCustomViewContainer(id: string, title: string, icon: URI, order: number, extensionId: ExtensionIdentifier | undefined, location: ViewContainerLocation): ViewContainer {
private registerCustomViewContainer(id: string, title: string, icon: URI | string, order: number, extensionId: ExtensionIdentifier | undefined, location: ViewContainerLocation): ViewContainer {
let viewContainer = this.viewContainersRegistry.get(id);
if (!viewContainer) {
@@ -72,6 +72,10 @@ const configurationEntrySchema: IJSONSchema = {
deprecationMessage: {
type: 'string',
description: nls.localize('scope.deprecationMessage', 'If set, the property is marked as deprecated and the given message is shown as an explanation.')
},
markdownDeprecationMessage: {
type: 'string',
description: nls.localize('scope.markdownDeprecationMessage', 'If set, the property is marked as deprecated and the given message is shown as an explanation in the markdown format.')
}
}
}
@@ -75,6 +75,7 @@ import { IExtHostApiDeprecationService } from 'vs/workbench/api/common/extHostAp
import { ExtHostAuthentication } from 'vs/workbench/api/common/extHostAuthentication';
import { ExtHostTimeline } from 'vs/workbench/api/common/extHostTimeline';
import { ExtHostNotebookConcatDocument } from 'vs/workbench/api/common/extHostNotebookConcatDocument';
import { IExtensionStoragePaths } from 'vs/workbench/api/common/extHostStoragePaths';
export interface IExtensionApiFactory {
(extension: IExtensionDescription, registry: ExtensionDescriptionRegistry, configProvider: ExtHostConfigProvider): typeof vscode;
@@ -93,6 +94,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
const uriTransformer = accessor.get(IURITransformerService);
const rpcProtocol = accessor.get(IExtHostRpcService);
const extHostStorage = accessor.get(IExtHostStorage);
const extensionStoragePaths = accessor.get(IExtensionStoragePaths);
const extHostLogService = accessor.get(ILogService);
const extHostTunnelService = accessor.get(IExtHostTunnelService);
const extHostApiDeprecation = accessor.get(IExtHostApiDeprecationService);
@@ -137,7 +139,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
const extHostTheming = rpcProtocol.set(ExtHostContext.ExtHostTheming, new ExtHostTheming(rpcProtocol));
const extHostAuthentication = rpcProtocol.set(ExtHostContext.ExtHostAuthentication, new ExtHostAuthentication(rpcProtocol));
const extHostTimeline = rpcProtocol.set(ExtHostContext.ExtHostTimeline, new ExtHostTimeline(rpcProtocol, extHostCommands));
const extHostWebviews = rpcProtocol.set(ExtHostContext.ExtHostWebviews, new ExtHostWebviews(rpcProtocol, initData.environment, extHostWorkspace, extHostLogService, extHostApiDeprecation, extHostDocuments));
const extHostWebviews = rpcProtocol.set(ExtHostContext.ExtHostWebviews, new ExtHostWebviews(rpcProtocol, initData.environment, extHostWorkspace, extHostLogService, extHostApiDeprecation, extHostDocuments, extensionStoragePaths));
// Check that no named customers are missing
// {{SQL CARBON EDIT}} filter out the services we don't expose
@@ -203,6 +205,9 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
login(providerId: string, scopes: string[]): Thenable<vscode.AuthenticationSession> {
return extHostAuthentication.login(extension, providerId, scopes);
},
logout(providerId: string, sessionId: string): Thenable<void> {
return extHostAuthentication.logout(providerId, sessionId);
},
get onDidChangeSessions(): Event<{ [providerId: string]: vscode.AuthenticationSessionsChangeEvent }> {
return extHostAuthentication.onDidChangeSessions;
},
@@ -489,10 +494,6 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
checkProposedApiEnabled(extension);
return extHostTerminalService.onDidWriteTerminalData(listener, thisArg, disposables);
},
getEnvironmentVariableCollection(persistent?: boolean): vscode.EnvironmentVariableCollection {
checkProposedApiEnabled(extension);
return extHostTerminalService.getEnvironmentVariableCollection(extension, persistent);
},
get state() {
return extHostWindow.state;
},
@@ -589,7 +590,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
registerCustomEditorProvider: (viewType: string, provider: vscode.CustomTextEditorProvider, options: { webviewOptions?: vscode.WebviewPanelOptions } = {}) => {
return extHostWebviews.registerCustomEditorProvider(extension, viewType, provider, options);
},
registerCustomEditorProvider2: (viewType: string, provider: vscode.CustomEditorProvider, options: { webviewOptions?: vscode.WebviewPanelOptions, supportsMultipleEditorsPerResource?: boolean } = {}) => {
registerCustomEditorProvider2: (viewType: string, provider: vscode.CustomReadonlyEditorProvider, options: { webviewOptions?: vscode.WebviewPanelOptions, supportsMultipleEditorsPerDocument?: boolean } = {}) => {
checkProposedApiEnabled(extension);
return extHostWebviews.registerCustomEditorProvider(extension, viewType, provider, options);
},
@@ -855,7 +856,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
extHostLogService.warn('Debug API is disabled in Azure Data Studio');
return undefined!;
},
registerDebugConfigurationProvider(debugType: string, provider: vscode.DebugConfigurationProvider, scope?: vscode.DebugConfigurationProviderScope) {
registerDebugConfigurationProvider(debugType: string, provider: vscode.DebugConfigurationProvider, triggerKind?: vscode.DebugConfigurationProviderTriggerKind) {
extHostLogService.warn('Debug API is disabled in Azure Data Studio');
return undefined!;
},
@@ -938,6 +939,10 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
checkProposedApiEnabled(extension);
return extHostNotebook.activeNotebookEditor;
},
onDidChangeNotebookDocument(listener, thisArgs?, disposables?) {
checkProposedApiEnabled(extension);
return extHostNotebook.onDidChangeNotebookDocument(listener, thisArgs, disposables);
},
createConcatTextDocument(notebook, selector) {
checkProposedApiEnabled(extension);
return new ExtHostNotebookConcatDocument(extHostNotebook, extHostDocuments, notebook, selector);
@@ -1065,7 +1070,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
CallHierarchyIncomingCall: extHostTypes.CallHierarchyIncomingCall,
CallHierarchyItem: extHostTypes.CallHierarchyItem,
DebugConsoleMode: extHostTypes.DebugConsoleMode,
DebugConfigurationProviderScope: extHostTypes.DebugConfigurationProviderScope,
DebugConfigurationProviderTriggerKind: extHostTypes.DebugConfigurationProviderTriggerKind,
Decoration: extHostTypes.Decoration,
UIKind: UIKind,
ColorThemeKind: extHostTypes.ColorThemeKind,
@@ -55,7 +55,7 @@ import { INotebookMimeTypeSelector, IOutput, INotebookDisplayOrder, NotebookCell
import { CallHierarchyItem } from 'vs/workbench/contrib/callHierarchy/common/callHierarchy';
import { Dto } from 'vs/base/common/types';
import { ISerializableEnvironmentVariableCollection } from 'vs/workbench/contrib/terminal/common/environmentVariable';
import { DebugConfigurationProviderScope } from 'vs/workbench/api/common/extHostTypes';
import { DebugConfigurationProviderTriggerKind } from 'vs/workbench/api/common/extHostTypes';
// {{SQL CARBON EDIT}}
import { ITreeItem as sqlITreeItem } from 'sql/workbench/common/views';
@@ -139,6 +139,7 @@ export interface MainThreadCommandsShape extends IDisposable {
export interface CommentProviderFeatures {
reactionGroup?: modes.CommentReaction[];
reactionHandler?: boolean;
options?: modes.CommentOptions;
}
export type CommentThreadChanges = Partial<{
@@ -616,7 +617,7 @@ export interface MainThreadWebviewsShape extends IDisposable {
$unregisterSerializer(viewType: string): void;
$registerTextEditorProvider(extension: WebviewExtensionDescription, viewType: string, options: modes.IWebviewPanelOptions, capabilities: CustomTextEditorCapabilities): void;
$registerCustomEditorProvider(extension: WebviewExtensionDescription, viewType: string, options: modes.IWebviewPanelOptions, supportsMultipleEditorsPerResource: boolean): void;
$registerCustomEditorProvider(extension: WebviewExtensionDescription, viewType: string, options: modes.IWebviewPanelOptions, supportsMultipleEditorsPerDocument: boolean): void;
$unregisterEditorProvider(viewType: string): void;
$onDidEdit(resource: UriComponents, viewType: string, editId: number, label: string | undefined): void;
@@ -850,7 +851,7 @@ export interface MainThreadDebugServiceShape extends IDisposable {
$acceptDAMessage(handle: number, message: DebugProtocol.ProtocolMessage): void;
$acceptDAError(handle: number, name: string, message: string, stack: string | undefined): void;
$acceptDAExit(handle: number, code: number | undefined, signal: string | undefined): void;
$registerDebugConfigurationProvider(type: string, scope: DebugConfigurationProviderScope, hasProvideMethod: boolean, hasResolveMethod: boolean, hasResolve2Method: boolean, hasProvideDaMethod: boolean, handle: number): Promise<void>;
$registerDebugConfigurationProvider(type: string, triggerKind: DebugConfigurationProviderTriggerKind, hasProvideMethod: boolean, hasResolveMethod: boolean, hasResolve2Method: boolean, hasProvideDaMethod: boolean, handle: number): Promise<void>;
$registerDebugAdapterDescriptorFactory(type: string, handle: number): Promise<void>;
$unregisterDebugConfigurationProvider(handle: number): void;
$unregisterDebugAdapterDescriptorFactory(handle: number): void;

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