Merge from vscode cbeff45f80213db0ddda2183170281ed97ed3b12 (#8670)

* Merge from vscode cbeff45f80213db0ddda2183170281ed97ed3b12

* fix null strict checks
This commit is contained in:
Anthony Dresser
2019-12-13 00:50:37 -08:00
committed by GitHub
parent 67abc2f690
commit 642920504a
136 changed files with 2918 additions and 1729 deletions
-1
View File
@@ -62,7 +62,6 @@ export interface ICommandHandler {
#includeAll(vs/editor/common/editorCommon;editorOptions.=>): IScrollEvent
#includeAll(vs/editor/common/model/textModelEvents):
#includeAll(vs/editor/common/controller/cursorEvents):
#include(vs/platform/accessibility/common/accessibility): AccessibilitySupport
#includeAll(vs/editor/common/config/editorOptions):
#includeAll(vs/editor/browser/editorBrowser;editorCommon.=>;editorOptions.=>):
#include(vs/editor/common/config/fontInfo): FontInfo, BareFontInfo
+10
View File
@@ -46,6 +46,16 @@
}
],
"menus": {
"commandPalette": [
{
"command": "searchResult.rerunSearch",
"when": "false"
},
{
"command": "searchResult.rerunSearchWithContext",
"when": "false"
}
],
"editor/title": [
{
"command": "searchResult.rerunSearch",
@@ -24,5 +24,34 @@
"mocha-junit-reporter": "^1.17.0",
"mocha-multi-reporters": "^1.1.7",
"vscode": "1.1.5"
},
"contributes": {
"tokenTypes": [
{
"id": "testToken",
"description": "A test token"
}
],
"tokenModifiers": [
{
"id": "testModifier",
"description": "A test modifier"
}
],
"tokenStyleDefaults": [
{
"selector": "testToken.testModifier",
"light": {
"fontStyle": "bold"
},
"dark": {
"fontStyle": "bold"
},
"highContrast": {
"fontStyle": "bold"
}
}
]
}
}
@@ -8,11 +8,13 @@ import * as jsoncParser from 'jsonc-parser';
export function activate(context: vscode.ExtensionContext): any {
const tokenTypes = ['type', 'struct', 'class', 'interface', 'enum', 'parameterType', 'function', 'variable'];
const tokenModifiers = ['static', 'abstract', 'deprecated', 'declaration', 'documentation', 'member', 'async'];
const tokenTypes = ['type', 'struct', 'class', 'interface', 'enum', 'parameterType', 'function', 'variable', 'testToken'];
const tokenModifiers = ['static', 'abstract', 'deprecated', 'declaration', 'documentation', 'member', 'async', 'testModifier'];
const legend = new vscode.SemanticTokensLegend(tokenTypes, tokenModifiers);
const outputChannel = vscode.window.createOutputChannel('Semantic Tokens Test');
const semanticHighlightProvider: vscode.SemanticTokensProvider = {
provideSemanticTokens(document: vscode.TextDocument): vscode.ProviderResult<vscode.SemanticTokens> {
const builder = new vscode.SemanticTokensBuilder();
@@ -35,8 +37,13 @@ export function activate(context: vscode.ExtensionContext): any {
builder.push(startLine, startCharacter, length, tokenType, tokenModifiers);
const selectedModifiers = legend.tokenModifiers.filter((_val, bit) => tokenModifiers & (1 << bit)).join(' ');
outputChannel.appendLine(`line: ${startLine}, character: ${startCharacter}, length ${length}, ${legend.tokenTypes[tokenType]} (${tokenType}), ${selectedModifiers} ${tokenModifiers.toString(2)}`);
}
outputChannel.appendLine('---');
const visitor: jsoncParser.JSONVisitor = {
onObjectProperty: (property: string, _offset: number, _length: number, startLine: number, startCharacter: number) => {
addToken(property, startLine, startCharacter, property.length + 2);
@@ -0,0 +1,13 @@
{
"editor.tokenColorCustomizationsExperimental": {
"class": "#00b0b0",
"interface": "#845faf",
"function": "#ff00ff",
"*.declaration": {
"fontStyle": "underline"
},
"*.declaration.member": {
"fontStyle": "italic bold",
}
}
}
@@ -0,0 +1,9 @@
[
"class", "function.member.declaration",
"parameterType.declaration", "type", "parameterType.declaration", "type",
"variable.declaration", "parameterNames",
"function.member.declaration",
"interface.declaration",
"function.member.declaration", "testToken.testModifier"
]
@@ -109,7 +109,7 @@ export class ConnectionConfig {
return Promise.resolve(profile.groupId);
} else {
let groups = this.configurationService.inspect<IConnectionProfileGroup[]>(GROUPS_CONFIG_KEY).user;
let result = this.saveGroup(groups, profile.groupFullName, undefined, undefined);
let result = this.saveGroup(groups!, profile.groupFullName, undefined, undefined);
groups = result.groups;
return this.configurationService.updateValue(GROUPS_CONFIG_KEY, groups, ConfigurationTarget.USER).then(() => result.newGroupId!);
@@ -129,7 +129,7 @@ export class ConnectionConfig {
let errMessage: string = nls.localize('invalidServerName', "A server group with the same name already exists.");
return Promise.reject(errMessage);
} else {
let result = this.saveGroup(groups, profileGroup.name, profileGroup.color, profileGroup.description);
let result = this.saveGroup(groups!, profileGroup.name, profileGroup.color, profileGroup.description);
groups = result.groups;
return this.configurationService.updateValue(GROUPS_CONFIG_KEY, groups, ConfigurationTarget.USER).then(() => result.newGroupId!);
@@ -220,7 +220,7 @@ export class ConnectionConfig {
// Get all connections in the settings
let profiles = this.configurationService.inspect<IConnectionProfileStore[]>(CONNECTIONS_CONFIG_KEY).user;
// Remove the profile from the connections
profiles = profiles.filter(value => {
profiles = profiles!.filter(value => {
let providerConnectionProfile = ConnectionProfile.createFromStoredProfile(value, this._capabilitiesService);
return providerConnectionProfile.getOptionsKey() !== profile.getOptionsKey();
});
@@ -241,7 +241,7 @@ export class ConnectionConfig {
// Get all connections in the settings
let profiles = this.configurationService.inspect<IConnectionProfileStore[]>(CONNECTIONS_CONFIG_KEY).user;
// Remove the profiles from the connections
profiles = profiles.filter(value => {
profiles = profiles!.filter(value => {
let providerConnectionProfile = ConnectionProfile.createFromStoredProfile(value, this._capabilitiesService);
return !connections.some((val) => val.getOptionsKey() === providerConnectionProfile.getOptionsKey());
});
@@ -249,7 +249,7 @@ export class ConnectionConfig {
// Get all groups in the settings
let groups = this.configurationService.inspect<IConnectionProfileGroup[]>(GROUPS_CONFIG_KEY).user;
// Remove subgroups in the settings
groups = groups.filter((grp) => {
groups = groups!.filter((grp) => {
return !subgroups.some((item) => item.id === grp.id);
});
return Promise.all([
@@ -263,7 +263,7 @@ export class ConnectionConfig {
*/
public changeGroupIdForConnectionGroup(source: ConnectionProfileGroup, target: ConnectionProfileGroup): Promise<void> {
let groups = this.configurationService.inspect<IConnectionProfileGroup[]>(GROUPS_CONFIG_KEY).user;
groups = groups.map(g => {
groups = groups!.map(g => {
if (g.id === source.id) {
g.parentId = target.id;
}
@@ -334,7 +334,7 @@ export class ConnectionConfig {
let errMessage: string = nls.localize('invalidServerName', "A server group with the same name already exists.");
return Promise.reject(errMessage);
}
groups = groups.map(g => {
groups = groups!.map(g => {
if (g.id === source.id) {
g.name = source.name;
g.description = source.description;
@@ -14,7 +14,7 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { IThemeService } from 'vs/platform/theme/common/themeService';
import { IStorageService } from 'vs/platform/storage/common/storage';
import { ITextResourceConfigurationService } from 'vs/editor/common/services/resourceConfiguration';
import { IResourceConfigurationService } from 'vs/editor/common/services/resourceConfiguration';
import { EditorOptions } from 'vs/workbench/common/editor';
import { StandaloneCodeEditor } from 'vs/editor/standalone/browser/standaloneCodeEditor';
import { CancellationToken } from 'vs/base/common/cancellation';
@@ -43,7 +43,7 @@ export class QueryTextEditor extends BaseTextEditor {
@ITelemetryService telemetryService: ITelemetryService,
@IInstantiationService instantiationService: IInstantiationService,
@IStorageService storageService: IStorageService,
@ITextResourceConfigurationService configurationService: ITextResourceConfigurationService,
@IResourceConfigurationService configurationService: IResourceConfigurationService,
@IThemeService themeService: IThemeService,
@IEditorGroupsService editorGroupService: IEditorGroupsService,
@IEditorService protected editorService: IEditorService,
@@ -14,7 +14,7 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { IThemeService } from 'vs/platform/theme/common/themeService';
import { IStorageService } from 'vs/platform/storage/common/storage';
import { ITextResourceConfigurationService } from 'vs/editor/common/services/resourceConfiguration';
import { IResourceConfigurationService } from 'vs/editor/common/services/resourceConfiguration';
import { EditorOptions } from 'vs/workbench/common/editor';
import { StandaloneCodeEditor } from 'vs/editor/standalone/browser/standaloneCodeEditor';
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
@@ -43,7 +43,7 @@ export class ProfilerResourceEditor extends BaseTextEditor {
@ITelemetryService telemetryService: ITelemetryService,
@IInstantiationService instantiationService: IInstantiationService,
@IStorageService storageService: IStorageService,
@ITextResourceConfigurationService configurationService: ITextResourceConfigurationService,
@IResourceConfigurationService configurationService: IResourceConfigurationService,
@IThemeService themeService: IThemeService,
@IEditorService protected editorService: IEditorService,
@IEditorGroupsService editorGroupService: IEditorGroupsService
+3 -3
View File
@@ -232,9 +232,9 @@ class DomListener implements IDisposable {
export function addDisposableListener<K extends keyof GlobalEventHandlersEventMap>(node: EventTarget, type: K, handler: (event: GlobalEventHandlersEventMap[K]) => void, useCapture?: boolean): IDisposable;
export function addDisposableListener(node: EventTarget, type: string, handler: (event: any) => void, useCapture?: boolean): IDisposable;
export function addDisposableListener(node: EventTarget, type: string, handler: (event: any) => void, useCapture: AddEventListenerOptions): IDisposable;
export function addDisposableListener(node: EventTarget, type: string, handler: (event: any) => void, useCapture?: boolean | AddEventListenerOptions): IDisposable {
return new DomListener(node, type, handler, useCapture);
export function addDisposableListener(node: EventTarget, type: string, handler: (event: any) => void, options: AddEventListenerOptions): IDisposable;
export function addDisposableListener(node: EventTarget, type: string, handler: (event: any) => void, useCaptureOrOptions?: boolean | AddEventListenerOptions): IDisposable {
return new DomListener(node, type, handler, useCaptureOrOptions);
}
export interface IAddStandardDisposableListenerSignature {
@@ -5,7 +5,7 @@
@font-face {
font-family: "codicon";
src: url("./codicon.ttf?c4e66586cd3ad4acc55fc456c0760dec") format("truetype");
src: url("./codicon.ttf?7ed98366b9684aff02478216decd2fe9") format("truetype");
}
.codicon[class*='codicon-'] {
@@ -396,6 +396,7 @@
.codicon-debug-breakpoint-function:before { content: "\eb88" }
.codicon-debug-breakpoint-function-disabled:before { content: "\eb88" }
.codicon-debug-stackframe-active:before { content: "\eb89" }
.codicon-debug-stackframe-dot:before { content: "\eb8a" }
.codicon-debug-stackframe:before { content: "\eb8b" }
.codicon-debug-stackframe-focused:before { content: "\eb8b" }
.codicon-debug-breakpoint-unsupported:before { content: "\eb8c" }
@@ -404,4 +405,7 @@
.codicon-debug-step-back:before { content: "\eb8f" }
.codicon-debug-restart-frame:before { content: "\eb90" }
.codicon-debug-alternate:before { content: "\eb91" }
.codicon-call-incoming:before { content: "\eb92" }
.codicon-call-outgoing:before { content: "\eb93" }
.codicon-menu:before { content: "\eb94" }
.codicon-debug-alt:before { content: "\f101" }
-3
View File
@@ -1,3 +0,0 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M14.4315 3.3232L5.96151 13.3232L5.1708 13.2874L1.8208 8.5174L2.63915 7.94268L5.61697 12.1827L13.6684 2.67688L14.4315 3.3232Z" fill="#C5C5C5"/>
</svg>

Before

Width:  |  Height:  |  Size: 295 B

-5
View File
@@ -1,5 +0,0 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M4 8C4 8.19778 3.94135 8.39112 3.83147 8.55557C3.72159 8.72002 3.56541 8.84819 3.38268 8.92388C3.19996 8.99957 2.99889 9.01937 2.80491 8.98079C2.61093 8.9422 2.43275 8.84696 2.29289 8.70711C2.15304 8.56725 2.0578 8.38907 2.01922 8.19509C1.98063 8.00111 2.00043 7.80004 2.07612 7.61732C2.15181 7.43459 2.27998 7.27841 2.44443 7.16853C2.60888 7.05865 2.80222 7 3 7C3.26522 7 3.51957 7.10536 3.70711 7.29289C3.89464 7.48043 4 7.73478 4 8Z" fill="#C5C5C5"/>
<path d="M9 8C9 8.19778 8.94135 8.39112 8.83147 8.55557C8.72159 8.72002 8.56541 8.84819 8.38268 8.92388C8.19996 8.99957 7.99889 9.01937 7.80491 8.98079C7.61093 8.9422 7.43275 8.84696 7.29289 8.70711C7.15304 8.56725 7.0578 8.38907 7.01922 8.19509C6.98063 8.00111 7.00043 7.80004 7.07612 7.61732C7.15181 7.43459 7.27998 7.27841 7.44443 7.16853C7.60888 7.05865 7.80222 7 8 7C8.26522 7 8.51957 7.10536 8.70711 7.29289C8.89464 7.48043 9 7.73478 9 8Z" fill="#C5C5C5"/>
<path d="M14 8C14 8.19778 13.9414 8.39112 13.8315 8.55557C13.7216 8.72002 13.5654 8.84819 13.3827 8.92388C13.2 8.99957 12.9989 9.01937 12.8049 8.98079C12.6109 8.9422 12.4327 8.84696 12.2929 8.70711C12.153 8.56725 12.0578 8.38907 12.0192 8.19509C11.9806 8.00111 12.0004 7.80004 12.0761 7.61732C12.1518 7.43459 12.28 7.27841 12.4444 7.16853C12.6089 7.05865 12.8022 7 13 7C13.2652 7 13.5196 7.10536 13.7071 7.29289C13.8946 7.48043 14 7.73478 14 8Z" fill="#C5C5C5"/>
</svg>

Before

Width:  |  Height:  |  Size: 1.5 KiB

+20 -16
View File
@@ -51,8 +51,17 @@
.monaco-menu .monaco-action-bar.vertical .submenu-indicator {
height: 100%;
mask: url('submenu.svg') no-repeat 90% 50%/13px 13px;
-webkit-mask: url('submenu.svg') no-repeat 90% 50%/13px 13px;
}
.monaco-menu .monaco-action-bar.vertical .submenu-indicator.codicon {
font-size: 16px;
display: flex;
align-items: center;
}
.monaco-menu .monaco-action-bar.vertical .submenu-indicator.codicon::before {
margin-left: auto;
margin-right: -20px;
}
.monaco-menu .monaco-action-bar.vertical .action-item.disabled .keybinding,
@@ -94,14 +103,15 @@
.monaco-menu .monaco-action-bar.vertical .menu-item-check {
position: absolute;
visibility: hidden;
mask: url('check.svg') no-repeat 50% 56%/15px 15px;
-webkit-mask: url('check.svg') no-repeat 50% 56%/15px 15px;
width: 1em;
height: 100%;
}
.monaco-menu .monaco-action-bar.vertical .action-menu-item.checked .menu-item-check {
visibility: visible;
display: flex;
align-items: center;
justify-content: center;
}
/* Context Menu */
@@ -186,9 +196,6 @@
}
.menubar .toolbar-toggle-more {
background-position: center;
background-repeat: no-repeat;
background-size: 14px;
width: 20px;
height: 100%;
}
@@ -197,21 +204,18 @@
position: absolute;
left: 0px;
top: 0px;
background-position: center;
background-repeat: no-repeat;
background-size: 16px;
cursor: pointer;
width: 100%;
display: flex;
align-items: center;
justify-content: center;
}
.menubar .toolbar-toggle-more {
display: inline-block;
padding: 0;
mask: url('ellipsis.svg') no-repeat 50% 55%/14px 14px;
-webkit-mask: url('ellipsis.svg') no-repeat 50% 55%/14px 14px;
vertical-align: sub;
}
.menubar.compact .toolbar-toggle-more {
mask: url('menu.svg') no-repeat 50% 55%/16px 16px;
-webkit-mask: url('menu.svg') no-repeat 50% 55%/16px 16px;
.menubar.compact .toolbar-toggle-more::before {
content: "\eb94" !important;
}
+4 -4
View File
@@ -422,7 +422,7 @@ class BaseMenuActionViewItem extends BaseActionViewItem {
}
}
this.check = append(this.item, $('span.menu-item-check'));
this.check = append(this.item, $('span.menu-item-check.codicon.codicon-check'));
this.check.setAttribute('role', 'none');
this.label = append(this.item, $('span.action-label'));
@@ -602,7 +602,7 @@ class BaseMenuActionViewItem extends BaseActionViewItem {
}
if (this.check) {
this.check.style.backgroundColor = fgColor ? `${fgColor}` : '';
this.check.style.color = fgColor ? `${fgColor}` : '';
}
if (this.container) {
@@ -662,7 +662,7 @@ class SubmenuMenuActionViewItem extends BaseMenuActionViewItem {
addClass(this.item, 'monaco-submenu-item');
this.item.setAttribute('aria-haspopup', 'true');
this.updateAriaExpanded('false');
this.submenuIndicator = append(this.item, $('span.submenu-indicator'));
this.submenuIndicator = append(this.item, $('span.submenu-indicator.codicon.codicon-chevron-right'));
this.submenuIndicator.setAttribute('aria-hidden', 'true');
}
@@ -822,7 +822,7 @@ class SubmenuMenuActionViewItem extends BaseMenuActionViewItem {
const fgColor = isSelected && this.menuStyle.selectionForegroundColor ? this.menuStyle.selectionForegroundColor : this.menuStyle.foregroundColor;
if (this.submenuIndicator) {
this.submenuIndicator.style.backgroundColor = fgColor ? `${fgColor}` : '';
this.submenuIndicator.style.color = fgColor ? `${fgColor}` : '';
}
if (this.parentData.submenu) {
+1 -1
View File
@@ -310,7 +310,7 @@ export class MenuBar extends Disposable {
createOverflowMenu(): void {
const label = this.options.compactMode !== undefined ? nls.localize('mAppMenu', 'Application Menu') : nls.localize('mMore', "...");
const buttonElement = $('div.menubar-menu-button', { 'role': 'menuitem', 'tabindex': -1, 'aria-label': label, 'title': label, 'aria-haspopup': true });
const titleElement = $('div.menubar-menu-title.toolbar-toggle-more', { 'role': 'none', 'aria-hidden': true });
const titleElement = $('div.menubar-menu-title.toolbar-toggle-more.codicon.codicon-more', { 'role': 'none', 'aria-hidden': true });
buttonElement.appendChild(titleElement);
this.container.appendChild(buttonElement);
-3
View File
@@ -1,3 +0,0 @@
<svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M4.52051 12.3643L9.87793 7L4.52051 1.635742L5.13574 1.0205078L11.1221 7L5.13574 12.9795L4.52051 12.3643Z" fill="black"/>
</svg>

Before

Width:  |  Height:  |  Size: 233 B

@@ -317,7 +317,7 @@ export abstract class AbstractScrollableElement extends Widget {
this._onMouseWheel(new StandardWheelEvent(browserEvent));
};
this._mouseWheelToDispose.push(dom.addDisposableListener(this._listenOnDomNode, isEdgeOrIE ? 'mousewheel' : 'wheel', onMouseWheel));
this._mouseWheelToDispose.push(dom.addDisposableListener(this._listenOnDomNode, isEdgeOrIE ? 'mousewheel' : 'wheel', onMouseWheel, { passive: false }));
}
}
@@ -9,6 +9,7 @@ import { Event } from 'vs/base/common/event';
import { Widget } from 'vs/base/browser/ui/widget';
import { Color } from 'vs/base/common/color';
import { deepClone } from 'vs/base/common/objects';
import { IContentActionHandler } from 'vs/base/browser/formattedTextRenderer';
import { IContextViewProvider } from 'vs/base/browser/ui/contextview/contextview';
import { IListStyles } from 'vs/base/browser/ui/list/listWidget';
import { SelectBoxNative } from 'vs/base/browser/ui/selectBox/selectBoxNative';
@@ -47,6 +48,7 @@ export interface ISelectOptionItem {
decoratorRight?: string;
description?: string;
descriptionIsMarkdown?: boolean;
descriptionMarkdownActionHandler?: IContentActionHandler;
isDisabled?: boolean;
}
@@ -19,6 +19,7 @@ import { ScrollbarVisibility } from 'vs/base/common/scrollable';
import { ISelectBoxDelegate, ISelectOptionItem, ISelectBoxOptions, ISelectBoxStyles, ISelectData } from 'vs/base/browser/ui/selectBox/selectBox';
import { isMacintosh } from 'vs/base/common/platform';
import { renderMarkdown } from 'vs/base/browser/markdownRenderer';
import { IContentActionHandler } from 'vs/base/browser/formattedTextRenderer';
const $ = dom.$;
@@ -760,11 +761,16 @@ export class SelectBoxList extends Disposable implements ISelectBoxDelegate, ILi
.filter(() => this.selectList.length > 0)
.on(e => this.onMouseUp(e), this));
this._register(this.selectList.onDidBlur(_ => this.onListBlur()));
this._register(this.selectList.onMouseOver(e => typeof e.index !== 'undefined' && this.selectList.setFocus([e.index])));
this._register(this.selectList.onFocusChange(e => this.onListFocus(e)));
this._register(dom.addDisposableListener(this.selectDropDownContainer, dom.EventType.FOCUS_OUT, e => {
if (!this._isVisible || dom.isAncestor(e.relatedTarget as HTMLElement, this.selectDropDownContainer)) {
return;
}
this.onListBlur();
}));
this.selectList.getHTMLElement().setAttribute('aria-label', this.selectBoxOptions.ariaLabel || '');
this.selectList.getHTMLElement().setAttribute('aria-expanded', 'true');
@@ -836,7 +842,7 @@ export class SelectBoxList extends Disposable implements ISelectBoxDelegate, ILi
}
private renderDescriptionMarkdown(text: string): HTMLElement {
private renderDescriptionMarkdown(text: string, actionHandler?: IContentActionHandler): HTMLElement {
const cleanRenderedMarkdown = (element: Node) => {
for (let i = 0; i < element.childNodes.length; i++) {
const child = <Element>element.childNodes.item(i);
@@ -850,7 +856,7 @@ export class SelectBoxList extends Disposable implements ISelectBoxDelegate, ILi
}
};
const renderedMarkdown = renderMarkdown({ value: text });
const renderedMarkdown = renderMarkdown({ value: text }, { actionHandler });
renderedMarkdown.classList.add('select-box-description-markdown');
cleanRenderedMarkdown(renderedMarkdown);
@@ -872,7 +878,8 @@ export class SelectBoxList extends Disposable implements ISelectBoxDelegate, ILi
if (description) {
if (descriptionIsMarkdown) {
this.selectionDetailsPane.appendChild(this.renderDescriptionMarkdown(description));
const actionHandler = this.options[selectedIndex].descriptionMarkdownActionHandler;
this.selectionDetailsPane.appendChild(this.renderDescriptionMarkdown(description, actionHandler));
} else {
this.selectionDetailsPane.innerText = description;
}
@@ -885,7 +892,6 @@ export class SelectBoxList extends Disposable implements ISelectBoxDelegate, ILi
this._skipLayout = true;
this.contextViewProvider.layout();
this._skipLayout = false;
}
// List keyboard controller
+14 -7
View File
@@ -184,13 +184,14 @@ export class Delayer<T> implements IDisposable {
private timeout: any;
private completionPromise: Promise<any> | null;
private doResolve: ((value?: any | Promise<any>) => void) | null;
private doReject?: (err: any) => void;
private doReject: ((err: any) => void) | null;
private task: ITask<T | Promise<T>> | null;
constructor(public defaultDelay: number) {
this.timeout = null;
this.completionPromise = null;
this.doResolve = null;
this.doReject = null;
this.task = null;
}
@@ -205,16 +206,20 @@ export class Delayer<T> implements IDisposable {
}).then(() => {
this.completionPromise = null;
this.doResolve = null;
const task = this.task!;
this.task = null;
return task();
if (this.task) {
const task = this.task;
this.task = null;
return task();
}
return undefined;
});
}
this.timeout = setTimeout(() => {
this.timeout = null;
this.doResolve!(null);
if (this.doResolve) {
this.doResolve(null);
}
}, delay);
return this.completionPromise;
@@ -228,7 +233,9 @@ export class Delayer<T> implements IDisposable {
this.cancelTimeout();
if (this.completionPromise) {
this.doReject!(errors.canceled());
if (this.doReject) {
this.doReject(errors.canceled());
}
this.completionPromise = null;
}
}
+6 -6
View File
@@ -3,9 +3,9 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
const escapeCodiconsRegex = /(?<!\\)\$\([a-z0-9\-]+?(?:~[a-z0-9\-]*?)?\)/gi;
const escapeCodiconsRegex = /(\\)?\$\([a-z0-9\-]+?(?:~[a-z0-9\-]*?)?\)/gi;
export function escapeCodicons(text: string): string {
return text.replace(escapeCodiconsRegex, match => `\\${match}`);
return text.replace(escapeCodiconsRegex, (match, escaped) => escaped ? match : `\\${match}`);
}
const markdownEscapedCodiconsRegex = /\\\$\([a-z0-9\-]+?(?:~[a-z0-9\-]*?)?\)/gi;
@@ -14,15 +14,15 @@ export function markdownEscapeEscapedCodicons(text: string): string {
return text.replace(markdownEscapedCodiconsRegex, match => `\\${match}`);
}
const markdownUnescapeCodiconsRegex = /(?<!\\)\$\\\(([a-z0-9\-]+?(?:~[a-z0-9\-]*?)?)\\\)/gi;
const markdownUnescapeCodiconsRegex = /(\\)?\$\\\(([a-z0-9\-]+?(?:~[a-z0-9\-]*?)?)\\\)/gi;
export function markdownUnescapeCodicons(text: string): string {
return text.replace(markdownUnescapeCodiconsRegex, (_, codicon) => `$(${codicon})`);
return text.replace(markdownUnescapeCodiconsRegex, (match, escaped, codicon) => escaped ? match : `$(${codicon})`);
}
const renderCodiconsRegex = /(\\)?\$\((([a-z0-9\-]+?)(?:~([a-z0-9\-]*?))?)\)/gi;
export function renderCodicons(text: string): string {
return text.replace(renderCodiconsRegex, (_, escape, codicon, name, animation) => {
return escape
return text.replace(renderCodiconsRegex, (_, escaped, codicon, name, animation) => {
return escaped
? `$(${codicon})`
: `<span class="codicon codicon-${name}${animation ? ` codicon-animation-${animation}` : ''}"></span>`;
});
@@ -185,8 +185,16 @@ export class IssueReporter extends Disposable {
}
if (styles.inputErrorBorder) {
content.push(`.invalid-input, .invalid-input:focus { border: 1px solid ${styles.inputErrorBorder} !important; }`);
content.push(`.validation-error, .required-input { color: ${styles.inputErrorBorder}; }`);
content.push(`.invalid-input, .invalid-input:focus, .validation-error { border: 1px solid ${styles.inputErrorBorder} !important; }`);
content.push(`.required-input { color: ${styles.inputErrorBorder}; }`);
}
if (styles.inputErrorBackground) {
content.push(`.validation-error { background: ${styles.inputErrorBackground}; }`);
}
if (styles.inputErrorForeground) {
content.push(`.validation-error { color: ${styles.inputErrorForeground}; }`);
}
if (styles.inputActiveBorder) {
@@ -251,16 +251,9 @@ a {
text-decoration: none;
}
.invalid-input {
border: 1px solid #be1100;
}
.required-input, .validation-error {
color: #be1100;
}
.section .input-group .validation-error {
margin-left: 15%;
margin-left: calc(15% + 5px);
padding: 10px;
}
.section .inline-form-control, .section .inline-label {
+9
View File
@@ -114,6 +114,7 @@ export class CodeWindow extends Disposable implements ICodeWindow {
// Load window state
const [state, hasMultipleDisplays] = this.restoreWindowState(config.state);
this.windowState = state;
this.logService.trace('window#ctor: using window state', state);
// in case we are maximized or fullscreen, only show later after the call to maximize/fullscreen (see below)
const isFullscreenOrMaximized = (this.windowState.mode === WindowMode.Maximized || this.windowState.mode === WindowMode.Fullscreen);
@@ -782,10 +783,12 @@ export class CodeWindow extends Disposable implements ICodeWindow {
|| typeof state.width !== 'number'
|| typeof state.height !== 'number'
) {
this.logService.trace('window#validateWindowState: unexpected type of state values');
return undefined;
}
if (state.width <= 0 || state.height <= 0) {
this.logService.trace('window#validateWindowState: unexpected negative values');
return undefined;
}
@@ -793,6 +796,8 @@ export class CodeWindow extends Disposable implements ICodeWindow {
if (displays.length === 1) {
const displayWorkingArea = this.getWorkingArea(displays[0]);
if (displayWorkingArea) {
this.logService.trace('window#validateWindowState: 1 display', displayWorkingArea);
if (state.x < displayWorkingArea.x) {
state.x = displayWorkingArea.x; // prevent window from falling out of the screen to the left
}
@@ -825,6 +830,8 @@ export class CodeWindow extends Disposable implements ICodeWindow {
if (state.display && state.mode === WindowMode.Fullscreen) {
const display = displays.filter(d => d.id === state.display)[0];
if (display && typeof display.bounds?.x === 'number' && typeof display.bounds?.y === 'number') {
this.logService.trace('window#validateWindowState: restoring fullscreen to previous display');
const defaults = defaultWindowState(WindowMode.Fullscreen); // make sure we have good values when the user restores the window
defaults.x = display.bounds.x; // carefull to use displays x/y position so that the window ends up on the correct monitor
defaults.y = display.bounds.y;
@@ -845,6 +852,8 @@ export class CodeWindow extends Disposable implements ICodeWindow {
bounds.x + bounds.width > displayWorkingArea.x && // prevent window from falling out of the screen to the left
bounds.y + bounds.height > displayWorkingArea.y // prevent window from falling out of the scree nto the top
) {
this.logService.trace('window#validateWindowState: multi display', displayWorkingArea);
return state;
}
@@ -122,7 +122,7 @@ export class MouseHandler extends ViewEventHandler {
e.stopPropagation();
}
};
this._register(dom.addDisposableListener(this.viewHelper.viewDomNode, browser.isEdgeOrIE ? 'mousewheel' : 'wheel', onMouseWheel, true));
this._register(dom.addDisposableListener(this.viewHelper.viewDomNode, browser.isEdgeOrIE ? 'mousewheel' : 'wheel', onMouseWheel, { capture: true, passive: false }));
this._context.addEventHandler(this);
}
@@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" x="0px" y="0px" width="30" height="50" viewBox="0 0 30 50" style="enable-background:new 0 0 30 50;"><polygon style="fill:#FFFFFF;stroke:#000000;stroke-width:2;" points="29,2.4 3.8,27.6 14,27.6 6.4,43 12.6,45 20.2,29.8 29,36"/></svg>

Before

Width:  |  Height:  |  Size: 273 B

@@ -1,2 +0,0 @@
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 26 38" style="enable-background:new 0 0 26 38;" width="26" height="38"><style type="text/css">.st0{stroke:#FFFFFF;stroke-miterlimit:10;}</style> <title>flipped-cursor-mac</title><path class="st0" d="M10.6,33.2l3.2-9.4H4.2L25,2.4v28.8L19.4,26l-3.2,9.2c-0.4,1-1.6,1.6-2.6,1.2L12,35.8 C10.8,35.4,10.2,34.4,10.6,33.2z"/></svg>

Before

Width:  |  Height:  |  Size: 447 B

@@ -1 +0,0 @@
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 13 19" style="enable-background:new 0 0 13 19;" width="13" height="19"><style type="text/css">.st0{stroke:#FFFFFF;stroke-miterlimit:10;}</style><title>flipped-cursor-mac</title><path class="st0" d="M5.3,16.6l1.6-4.7H2.1L12.5,1.2v14.4L9.7,13l-1.6,4.6c-0.2,0.5-0.8,0.8-1.3,0.6L6,17.9 C5.4,17.7,5.1,17.2,5.3,16.6z"/></svg>

Before

Width:  |  Height:  |  Size: 443 B

@@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="15" height="25" x="0px" y="0px" viewBox="0 0 15 25" style="enable-background:new 0 0 15 25;"><polygon style="fill:#FFFFFF;stroke:#000000" points="14.5,1.2 1.9,13.8 7,13.8 3.2,21.5 6.3,22.5 10.1,14.9 14.5,18"/></svg>

Before

Width:  |  Height:  |  Size: 262 B

@@ -19,20 +19,6 @@
width: 100%;
}
.monaco-editor .margin-view-overlays .line-numbers {
cursor: -webkit-image-set(
url('flipped-cursor.svg') 1x,
url('flipped-cursor-2x.svg') 2x
) 30 0, default;
}
.monaco-editor.mac .margin-view-overlays .line-numbers {
cursor: -webkit-image-set(
url('flipped-cursor-mac.svg') 1x,
url('flipped-cursor-mac-2x.svg') 2x
) 24 3, default;
}
.monaco-editor .margin-view-overlays .line-numbers.lh-odd {
margin-top: 1px;
}
@@ -15,7 +15,7 @@
/* -- smooth-caret-animation -- */
.monaco-editor .cursors-layer.cursor-smooth-caret-animation > .cursor {
transition: 80ms;
transition: all 80ms;
}
/* -- block-outline-style -- */
@@ -85,4 +85,4 @@
.cursor-expand > .cursor {
animation: monaco-cursor-expand 0.5s ease-in-out 0s 20 alternate;
}
}
@@ -996,6 +996,7 @@ class EditorAccessibilitySupport extends BaseEditorOption<EditorOption.accessibi
/**
* The kind of animation in which the editor's cursor should be rendered.
* @internal
*/
export const enum TextEditorCursorBlinkingStyle {
/**
@@ -1040,6 +1041,7 @@ function _cursorBlinkingStyleFromString(cursorBlinkingStyle: 'blink' | 'smooth'
/**
* The style in which the editor's cursor should be rendered.
* @internal
*/
export enum TextEditorCursorStyle {
/**
@@ -1165,6 +1167,9 @@ export interface IEditorFindOptions {
globalFindClipboard?: boolean;
}
/**
* @internal
*/
export type EditorFindOptions = Readonly<Required<IEditorFindOptions>>;
class EditorFind extends BaseEditorOption<EditorOption.find, EditorFindOptions> {
@@ -1352,6 +1357,9 @@ export interface IGotoLocationOptions {
alternativeReferenceCommand?: string;
}
/**
* @internal
*/
export type GoToLocationOptions = Readonly<Required<IGotoLocationOptions>>;
class EditorGoToLocation extends BaseEditorOption<EditorOption.gotoLocation, GoToLocationOptions> {
@@ -1481,6 +1489,9 @@ export interface IEditorHoverOptions {
sticky?: boolean;
}
/**
* @internal
*/
export type EditorHoverOptions = Readonly<Required<IEditorHoverOptions>>;
class EditorHover extends BaseEditorOption<EditorOption.hover, EditorHoverOptions> {
@@ -1857,6 +1868,9 @@ export interface IEditorLightbulbOptions {
enabled?: boolean;
}
/**
* @internal
*/
export type EditorLightbulbOptions = Readonly<Required<IEditorLightbulbOptions>>;
class EditorLightbulb extends BaseEditorOption<EditorOption.lightbulb, EditorLightbulbOptions> {
@@ -1948,6 +1962,9 @@ export interface IEditorMinimapOptions {
scale?: number;
}
/**
* @internal
*/
export type EditorMinimapOptions = Readonly<Required<IEditorMinimapOptions>>;
class EditorMinimap extends BaseEditorOption<EditorOption.minimap, EditorMinimapOptions> {
@@ -2049,6 +2066,9 @@ export interface IEditorParameterHintOptions {
cycle?: boolean;
}
/**
* @internal
*/
export type InternalParameterHintOptions = Readonly<Required<IEditorParameterHintOptions>>;
class EditorParameterHints extends BaseEditorOption<EditorOption.parameterHints, InternalParameterHintOptions> {
@@ -2115,6 +2135,9 @@ export interface IQuickSuggestionsOptions {
strings: boolean;
}
/**
* @internal
*/
export type ValidQuickSuggestionsOptions = boolean | Readonly<Required<IQuickSuggestionsOptions>>;
class EditorQuickSuggestions extends BaseEditorOption<EditorOption.quickSuggestions, ValidQuickSuggestionsOptions> {
@@ -2191,6 +2214,9 @@ class EditorQuickSuggestions extends BaseEditorOption<EditorOption.quickSuggesti
export type LineNumbersType = 'on' | 'off' | 'relative' | 'interval' | ((lineNumber: number) => string);
/**
* @internal
*/
export const enum RenderLineNumbersType {
Off = 0,
On = 1,
@@ -2199,6 +2225,9 @@ export const enum RenderLineNumbersType {
Custom = 4
}
/**
* @internal
*/
export interface InternalEditorRenderLineNumbersOptions {
readonly renderType: RenderLineNumbersType;
readonly renderFn: ((lineNumber: number) => string) | null;
@@ -2354,6 +2383,9 @@ export interface IEditorScrollbarOptions {
horizontalSliderSize?: number;
}
/**
* @internal
*/
export interface InternalEditorScrollbarOptions {
readonly arrowSize: number;
readonly vertical: ScrollbarVisibility;
@@ -2568,6 +2600,9 @@ export interface ISuggestOptions {
showSnippets?: boolean;
}
/**
* @internal
*/
export type InternalSuggestOptions = Readonly<Required<ISuggestOptions>>;
class EditorSuggest extends BaseEditorOption<EditorOption.suggest, InternalSuggestOptions> {
@@ -2861,6 +2896,7 @@ class EditorTabFocusMode extends ComputedEditorOption<EditorOption.tabFocusMode,
/**
* Describes how to indent wrapped lines.
* @internal
*/
export const enum WrappingIndent {
/**
@@ -2894,6 +2930,9 @@ function _wrappingIndentFromString(wrappingIndent: 'none' | 'same' | 'indent' |
//#region wrappingInfo
/**
* @internal
*/
export interface EditorWrappingInfo {
readonly isDominatedByLongLines: boolean;
readonly isWordWrapMinified: boolean;
@@ -17,7 +17,7 @@ import { LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageCo
import { EditorSimpleWorker } from 'vs/editor/common/services/editorSimpleWorker';
import { IDiffComputationResult, IEditorWorkerService } from 'vs/editor/common/services/editorWorkerService';
import { IModelService } from 'vs/editor/common/services/modelService';
import { ITextResourceConfigurationService } from 'vs/editor/common/services/resourceConfiguration';
import { IResourceConfigurationService } from 'vs/editor/common/services/resourceConfiguration';
import { regExpFlags } from 'vs/base/common/strings';
import { isNonEmptyArray } from 'vs/base/common/arrays';
import { ILogService } from 'vs/platform/log/common/log';
@@ -52,7 +52,7 @@ export class EditorWorkerServiceImpl extends Disposable implements IEditorWorker
private readonly _logService: ILogService;
constructor(
@IModelService modelService: IModelService,
@ITextResourceConfigurationService configurationService: ITextResourceConfigurationService,
@IResourceConfigurationService configurationService: IResourceConfigurationService,
@ILogService logService: ILogService
) {
super();
@@ -129,14 +129,14 @@ export class EditorWorkerServiceImpl extends Disposable implements IEditorWorker
class WordBasedCompletionItemProvider implements modes.CompletionItemProvider {
private readonly _workerManager: WorkerManager;
private readonly _configurationService: ITextResourceConfigurationService;
private readonly _configurationService: IResourceConfigurationService;
private readonly _modelService: IModelService;
readonly _debugDisplayName = 'wordbasedCompletions';
constructor(
workerManager: WorkerManager,
configurationService: ITextResourceConfigurationService,
configurationService: IResourceConfigurationService,
modelService: IModelService
) {
this._workerManager = workerManager;
@@ -14,7 +14,7 @@ import { Range } from 'vs/editor/common/core/range';
import { DefaultEndOfLine, EndOfLinePreference, EndOfLineSequence, IIdentifiedSingleEditOperation, ITextBuffer, ITextBufferFactory, ITextModel, ITextModelCreationOptions } from 'vs/editor/common/model';
import { TextModel, createTextBuffer } from 'vs/editor/common/model/textModel';
import { IModelLanguageChangedEvent, IModelContentChangedEvent } from 'vs/editor/common/model/textModelEvents';
import { LanguageIdentifier, SemanticTokensProviderRegistry, SemanticTokensProvider, SemanticTokensLegend, SemanticTokens, SemanticTokensEdits } from 'vs/editor/common/modes';
import { LanguageIdentifier, SemanticTokensProviderRegistry, SemanticTokensProvider, SemanticTokensLegend, SemanticTokens, SemanticTokensEdits, TokenMetadata } from 'vs/editor/common/modes';
import { PLAINTEXT_LANGUAGE_IDENTIFIER } from 'vs/editor/common/modes/modesRegistry';
import { ILanguageSelection } from 'vs/editor/common/services/modeService';
import { IModelService } from 'vs/editor/common/services/modelService';
@@ -24,6 +24,7 @@ import { RunOnceScheduler } from 'vs/base/common/async';
import { CancellationTokenSource } from 'vs/base/common/cancellation';
import { SparseEncodedTokens, MultilineTokens2 } from 'vs/editor/common/model/tokensStore';
import { IThemeService } from 'vs/platform/theme/common/themeService';
import { ILogService, LogLevel } from 'vs/platform/log/common/log';
function MODEL_ID(resource: URI): string {
return resource.toString();
@@ -120,7 +121,8 @@ export class ModelServiceImpl extends Disposable implements IModelService {
constructor(
@IConfigurationService configurationService: IConfigurationService,
@ITextResourcePropertiesService resourcePropertiesService: ITextResourcePropertiesService,
@IThemeService themeService: IThemeService
@IThemeService themeService: IThemeService,
@ILogService logService: ILogService
) {
super();
this._configurationService = configurationService;
@@ -131,7 +133,7 @@ export class ModelServiceImpl extends Disposable implements IModelService {
this._configurationServiceSubscription = this._configurationService.onDidChangeConfiguration(e => this._updateModelOptions());
this._updateModelOptions();
this._register(new SemanticColoringFeature(this, themeService));
this._register(new SemanticColoringFeature(this, themeService, logService));
}
private static _readModelOptions(config: IRawConfig, isForSimpleWidget: boolean): ITextModelCreationOptions {
@@ -443,10 +445,10 @@ class SemanticColoringFeature extends Disposable {
private _watchers: Record<string, ModelSemanticColoring>;
private _semanticStyling: SemanticStyling;
constructor(modelService: IModelService, themeService: IThemeService) {
constructor(modelService: IModelService, themeService: IThemeService, logService: ILogService) {
super();
this._watchers = Object.create(null);
this._semanticStyling = this._register(new SemanticStyling(themeService));
this._semanticStyling = this._register(new SemanticStyling(themeService, logService));
this._register(modelService.onModelAdded((model) => {
this._watchers[model.uri.toString()] = new ModelSemanticColoring(model, themeService, this._semanticStyling);
}));
@@ -462,7 +464,8 @@ class SemanticStyling extends Disposable {
private _caches: WeakMap<SemanticTokensProvider, SemanticColoringProviderStyling>;
constructor(
private readonly _themeService: IThemeService
private readonly _themeService: IThemeService,
private readonly _logService: ILogService
) {
super();
this._caches = new WeakMap<SemanticTokensProvider, SemanticColoringProviderStyling>();
@@ -476,7 +479,7 @@ class SemanticStyling extends Disposable {
public get(provider: SemanticTokensProvider): SemanticColoringProviderStyling {
if (!this._caches.has(provider)) {
this._caches.set(provider, new SemanticColoringProviderStyling(provider.getLegend(), this._themeService));
this._caches.set(provider, new SemanticColoringProviderStyling(provider.getLegend(), this._themeService, this._logService));
}
return this._caches.get(provider)!;
}
@@ -581,7 +584,8 @@ class SemanticColoringProviderStyling {
constructor(
private readonly _legend: SemanticTokensLegend,
private readonly _themeService: IThemeService
private readonly _themeService: IThemeService,
private readonly _logService: ILogService
) {
this._hashTable = new HashTable();
}
@@ -605,6 +609,9 @@ class SemanticColoringProviderStyling {
if (typeof metadata === 'undefined') {
metadata = Constants.NO_STYLING;
}
if (this._logService.getLevel() === LogLevel.Trace) {
this._logService.trace(`getTokenStyleMetadata(${tokenType}${tokenModifiers.length ? ', ' + tokenModifiers.join(' ') : ''}): foreground: ${TokenMetadata.getForeground(metadata)}, fontStyle ${TokenMetadata.getFontStyle(metadata).toString(2)}`);
}
this._hashTable.add(tokenTypeIndex, tokenModifierSet, metadata);
return metadata;
@@ -6,19 +6,37 @@
import { Event } from 'vs/base/common/event';
import { URI } from 'vs/base/common/uri';
import { IPosition } from 'vs/editor/common/core/position';
import { IConfigurationChangeEvent } from 'vs/platform/configuration/common/configuration';
import { ConfigurationTarget } from 'vs/platform/configuration/common/configuration';
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
export const ITextResourceConfigurationService = createDecorator<ITextResourceConfigurationService>('textResourceConfigurationService');
export const IResourceConfigurationService = createDecorator<IResourceConfigurationService>('resourceConfigurationService');
export interface ITextResourceConfigurationService {
export interface IResourceConfigurationChangeEvent {
/**
* All affected keys. Also includes language overrides and keys changed under language overrides.
*/
readonly affectedKeys: string[];
/**
* Returns `true` if the given section has changed for the given resource.
*
* Example: To check if the configuration section has changed for a given resource use `e.affectsConfiguration(resource, section)`.
*
* @param resource Resource for which the configuration has to be checked.
* @param section Section of the configuration
*/
affectsConfiguration(resource: URI, section: string): boolean;
}
export interface IResourceConfigurationService {
_serviceBrand: undefined;
/**
* Event that fires when the configuration changes.
*/
onDidChangeConfiguration: Event<IConfigurationChangeEvent>;
onDidChangeConfiguration: Event<IResourceConfigurationChangeEvent>;
/**
* Fetches the value of the section for the given resource by applying language overrides.
@@ -32,6 +50,20 @@ export interface ITextResourceConfigurationService {
getValue<T>(resource: URI | undefined, section?: string): T;
getValue<T>(resource: URI | undefined, position?: IPosition, section?: string): T;
/**
* Update the configuration value for the given resource at the effective location.
*
* - If configurationTarget is not specified, target will be derived by checking where the configuration is defined.
* - If the language overrides for the give resource contains the configuration, then it is updated.
*
* @param resource Resource for which the configuration has to be updated
* @param key Configuration key
* @param value Configuration value
* @param configurationTarget Optional target into which the configuration has to be updated.
* If not specified, target will be derived by checking where the configuration is defined.
*/
updateValue(resource: URI, key: string, value: any, configurationTarget?: ConfigurationTarget): Promise<void>;
}
export const ITextResourcePropertiesService = createDecorator<ITextResourcePropertiesService>('textResourcePropertiesService');
@@ -44,4 +76,4 @@ export interface ITextResourcePropertiesService {
* Returns the End of Line characters for the given resource
*/
getEOL(resource: URI | undefined, language?: string): string;
}
}
@@ -9,15 +9,15 @@ import { URI } from 'vs/base/common/uri';
import { IPosition, Position } from 'vs/editor/common/core/position';
import { IModeService } from 'vs/editor/common/services/modeService';
import { IModelService } from 'vs/editor/common/services/modelService';
import { ITextResourceConfigurationService } from 'vs/editor/common/services/resourceConfiguration';
import { IConfigurationChangeEvent, IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { IResourceConfigurationService, IResourceConfigurationChangeEvent } from 'vs/editor/common/services/resourceConfiguration';
import { IConfigurationService, ConfigurationTarget, IConfigurationValue, IConfigurationChangeEvent } from 'vs/platform/configuration/common/configuration';
export class TextResourceConfigurationService extends Disposable implements ITextResourceConfigurationService {
export class TextResourceConfigurationService extends Disposable implements IResourceConfigurationService {
public _serviceBrand: undefined;
private readonly _onDidChangeConfiguration: Emitter<IConfigurationChangeEvent> = this._register(new Emitter<IConfigurationChangeEvent>());
public readonly onDidChangeConfiguration: Event<IConfigurationChangeEvent> = this._onDidChangeConfiguration.event;
private readonly _onDidChangeConfiguration: Emitter<IResourceConfigurationChangeEvent> = this._register(new Emitter<IResourceConfigurationChangeEvent>());
public readonly onDidChangeConfiguration: Event<IResourceConfigurationChangeEvent> = this._onDidChangeConfiguration.event;
constructor(
@IConfigurationService private readonly configurationService: IConfigurationService,
@@ -25,7 +25,7 @@ export class TextResourceConfigurationService extends Disposable implements ITex
@IModeService private readonly modeService: IModeService,
) {
super();
this._register(this.configurationService.onDidChangeConfiguration(e => this._onDidChangeConfiguration.fire(e)));
this._register(this.configurationService.onDidChangeConfiguration(e => this._onDidChangeConfiguration.fire(this.toResourceConfigurationChangeEvent(e))));
}
getValue<T>(resource: URI, section?: string): T;
@@ -37,6 +37,67 @@ export class TextResourceConfigurationService extends Disposable implements ITex
return this._getValue(resource, null, typeof arg2 === 'string' ? arg2 : undefined);
}
updateValue(resource: URI, key: string, value: any, configurationTarget?: ConfigurationTarget): Promise<void> {
const language = this.getLanguage(resource, null);
const configurationValue = this.configurationService.inspect(key, { resource, overrideIdentifier: language });
if (configurationTarget === undefined) {
configurationTarget = this.deriveConfigurationTarget(configurationValue, language);
}
switch (configurationTarget) {
case ConfigurationTarget.MEMORY:
return this._updateValue(key, value, configurationTarget, configurationValue.memoryTarget?.override, resource, language);
case ConfigurationTarget.WORKSPACE_FOLDER:
return this._updateValue(key, value, configurationTarget, configurationValue.workspaceFolderTarget?.override, resource, language);
case ConfigurationTarget.WORKSPACE:
return this._updateValue(key, value, configurationTarget, configurationValue.workspaceTarget?.override, resource, language);
case ConfigurationTarget.USER_REMOTE:
return this._updateValue(key, value, configurationTarget, configurationValue.userRemoteTarget?.override, resource, language);
default:
return this._updateValue(key, value, configurationTarget, configurationValue.userLocalTarget?.override, resource, language);
}
}
private _updateValue(key: string, value: any, configurationTarget: ConfigurationTarget, overriddenValue: any | undefined, resource: URI, language: string | null): Promise<void> {
if (language && overriddenValue !== undefined) {
return this.configurationService.updateValue(key, value, { resource, overrideIdentifier: language }, configurationTarget);
} else {
return this.configurationService.updateValue(key, value, { resource }, configurationTarget);
}
}
private deriveConfigurationTarget(configurationValue: IConfigurationValue<any>, language: string | null): ConfigurationTarget {
if (language) {
if (configurationValue.memoryTarget?.override !== undefined) {
return ConfigurationTarget.MEMORY;
}
if (configurationValue.workspaceFolderTarget?.override !== undefined) {
return ConfigurationTarget.WORKSPACE_FOLDER;
}
if (configurationValue.workspaceTarget?.override !== undefined) {
return ConfigurationTarget.WORKSPACE;
}
if (configurationValue.userRemoteTarget?.override !== undefined) {
return ConfigurationTarget.USER_REMOTE;
}
if (configurationValue.userLocalTarget?.override !== undefined) {
return ConfigurationTarget.USER_LOCAL;
}
}
if (configurationValue.memoryTarget?.value !== undefined) {
return ConfigurationTarget.MEMORY;
}
if (configurationValue.workspaceFolderTarget?.value !== undefined) {
return ConfigurationTarget.WORKSPACE_FOLDER;
}
if (configurationValue.workspaceTarget?.value !== undefined) {
return ConfigurationTarget.WORKSPACE;
}
if (configurationValue.userRemoteTarget?.value !== undefined) {
return ConfigurationTarget.USER_REMOTE;
}
return ConfigurationTarget.USER_LOCAL;
}
private _getValue<T>(resource: URI, position: IPosition | null, section: string | undefined): T {
const language = resource ? this.getLanguage(resource, position) : undefined;
if (typeof section === 'undefined') {
@@ -51,6 +112,15 @@ export class TextResourceConfigurationService extends Disposable implements ITex
return position ? this.modeService.getLanguageIdentifier(model.getLanguageIdAtPosition(position.lineNumber, position.column))!.language : model.getLanguageIdentifier().language;
}
return this.modeService.getModeIdByFilepathOrFirstLine(resource);
}
}
private toResourceConfigurationChangeEvent(configurationChangeEvent: IConfigurationChangeEvent): IResourceConfigurationChangeEvent {
return {
affectedKeys: configurationChangeEvent.affectedKeys,
affectsConfiguration: (resource: URI, configuration: string) => {
const overrideIdentifier = this.getLanguage(resource, null);
return configurationChangeEvent.affectsConfiguration(configuration, { resource, overrideIdentifier });
}
};
}
}
@@ -333,111 +333,12 @@ export enum CursorChangeReason {
Redo = 6
}
export enum AccessibilitySupport {
/**
* This should be the browser case where it is not known if a screen reader is attached or no.
*/
Unknown = 0,
Disabled = 1,
Enabled = 2
}
/**
* The kind of animation in which the editor's cursor should be rendered.
*/
export enum TextEditorCursorBlinkingStyle {
/**
* Hidden
*/
Hidden = 0,
/**
* Blinking
*/
Blink = 1,
/**
* Blinking with smooth fading
*/
Smooth = 2,
/**
* Blinking with prolonged filled state and smooth fading
*/
Phase = 3,
/**
* Expand collapse animation on the y axis
*/
Expand = 4,
/**
* No-Blinking
*/
Solid = 5
}
/**
* The style in which the editor's cursor should be rendered.
*/
export enum TextEditorCursorStyle {
/**
* As a vertical line (sitting between two characters).
*/
Line = 1,
/**
* As a block (sitting on top of a character).
*/
Block = 2,
/**
* As a horizontal line (sitting under a character).
*/
Underline = 3,
/**
* As a thin vertical line (sitting between two characters).
*/
LineThin = 4,
/**
* As an outlined block (sitting on top of a character).
*/
BlockOutline = 5,
/**
* As a thin horizontal line (sitting under a character).
*/
UnderlineThin = 6
}
export enum RenderMinimap {
None = 0,
Text = 1,
Blocks = 2
}
export enum RenderLineNumbersType {
Off = 0,
On = 1,
Relative = 2,
Interval = 3,
Custom = 4
}
/**
* Describes how to indent wrapped lines.
*/
export enum WrappingIndent {
/**
* No indentation => wrapped lines begin at column 1.
*/
None = 0,
/**
* Same => wrapped lines get the same indentation as the parent.
*/
Same = 1,
/**
* Indent => wrapped lines get +1 indentation toward the parent.
*/
Indent = 2,
/**
* DeepIndent => wrapped lines get +2 indentation toward the parent.
*/
DeepIndent = 3
}
/**
* A positioning preference for rendering content widgets.
*/
@@ -22,7 +22,7 @@ import { MarkerSeverity } from 'vs/platform/markers/common/markers';
import { IThemeService, registerThemingParticipant, ITheme, ICssStyleCollector } from 'vs/platform/theme/common/themeService';
import { registerColor, listErrorForeground, listWarningForeground, foreground } from 'vs/platform/theme/common/colorRegistry';
import { IdleValue } from 'vs/base/common/async';
import { ITextResourceConfigurationService } from 'vs/editor/common/services/resourceConfiguration';
import { IResourceConfigurationService } from 'vs/editor/common/services/resourceConfiguration';
import { URI } from 'vs/base/common/uri';
export type OutlineItem = OutlineGroup | OutlineElement;
@@ -282,7 +282,7 @@ export class OutlineFilter implements ITreeFilter<OutlineItem> {
constructor(
private readonly _prefix: string,
@ITextResourceConfigurationService private readonly _textResourceConfigService: ITextResourceConfigurationService,
@IResourceConfigurationService private readonly _textResourceConfigService: IResourceConfigurationService,
) { }
filter(element: OutlineItem): boolean {
@@ -18,6 +18,7 @@ import { CancellationToken } from 'vs/base/common/cancellation';
import { WordSelectionRangeProvider } from 'vs/editor/contrib/smartSelect/wordSelections';
import { TestTextResourcePropertiesService } from 'vs/editor/test/common/services/modelService.test';
import { TestThemeService } from 'vs/platform/theme/test/common/testThemeService';
import { NullLogService } from 'vs/platform/log/common/log';
class MockJSMode extends MockMode {
@@ -46,7 +47,7 @@ suite('SmartSelect', () => {
setup(() => {
const configurationService = new TestConfigurationService();
modelService = new ModelServiceImpl(configurationService, new TestTextResourcePropertiesService(configurationService), new TestThemeService());
modelService = new ModelServiceImpl(configurationService, new TestTextResourcePropertiesService(configurationService), new TestThemeService(), new NullLogService());
mode = new MockJSMode();
});
@@ -23,9 +23,9 @@ import { ITextModel, ITextSnapshot } from 'vs/editor/common/model';
import { TextEdit, WorkspaceEdit, isResourceTextEdit } from 'vs/editor/common/modes';
import { IModelService } from 'vs/editor/common/services/modelService';
import { IResolvedTextEditorModel, ITextModelContentProvider, ITextModelService } from 'vs/editor/common/services/resolverService';
import { ITextResourceConfigurationService, ITextResourcePropertiesService } from 'vs/editor/common/services/resourceConfiguration';
import { IResourceConfigurationService, ITextResourcePropertiesService, IResourceConfigurationChangeEvent } from 'vs/editor/common/services/resourceConfiguration';
import { CommandsRegistry, ICommand, ICommandEvent, ICommandHandler, ICommandService } from 'vs/platform/commands/common/commands';
import { IConfigurationChangeEvent, IConfigurationData, IConfigurationOverrides, IConfigurationService, IConfigurationModel } from 'vs/platform/configuration/common/configuration';
import { IConfigurationChangeEvent, IConfigurationData, IConfigurationOverrides, IConfigurationService, IConfigurationModel, IConfigurationValue, ConfigurationTarget } from 'vs/platform/configuration/common/configuration';
import { Configuration, ConfigurationModel, DefaultConfigurationModel } from 'vs/platform/configuration/common/configurationModels';
import { ContextKeyExpr, IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
import { IConfirmation, IConfirmationResult, IDialogOptions, IDialogService, IShowResult } from 'vs/platform/dialogs/common/dialogs';
@@ -460,13 +460,7 @@ export class SimpleConfigurationService implements IConfigurationService {
return Promise.resolve();
}
public inspect<C>(key: string, options: IConfigurationOverrides = {}): {
default: C,
user: C,
workspace?: C,
workspaceFolder?: C
value: C,
} {
public inspect<C>(key: string, options: IConfigurationOverrides = {}): IConfigurationValue<C> {
return this.configuration().inspect<C>(key, options, undefined);
}
@@ -493,16 +487,16 @@ export class SimpleConfigurationService implements IConfigurationService {
}
}
export class SimpleResourceConfigurationService implements ITextResourceConfigurationService {
export class SimpleResourceConfigurationService implements IResourceConfigurationService {
_serviceBrand: undefined;
private readonly _onDidChangeConfiguration = new Emitter<IConfigurationChangeEvent>();
private readonly _onDidChangeConfiguration = new Emitter<IResourceConfigurationChangeEvent>();
public readonly onDidChangeConfiguration = this._onDidChangeConfiguration.event;
constructor(private readonly configurationService: SimpleConfigurationService) {
this.configurationService.onDidChangeConfiguration((e) => {
this._onDidChangeConfiguration.fire(e);
this._onDidChangeConfiguration.fire({ affectedKeys: e.affectedKeys, affectsConfiguration: (resource: URI, configuration: string) => e.affectsConfiguration(configuration) });
});
}
@@ -516,6 +510,10 @@ export class SimpleResourceConfigurationService implements ITextResourceConfigur
}
return this.configurationService.getValue<T>(section);
}
updateValue(resource: URI, key: string, value: any, configurationTarget?: ConfigurationTarget): Promise<void> {
return this.configurationService.updateValue(key, value, { resource }, configurationTarget);
}
}
export class SimpleResourcePropertiesService implements ITextResourcePropertiesService {
@@ -346,9 +346,7 @@ export function createMonacoEditorAPI(): typeof monaco.editor {
remeasureFonts: remeasureFonts,
// enums
AccessibilitySupport: standaloneEnums.AccessibilitySupport,
ScrollbarVisibility: standaloneEnums.ScrollbarVisibility,
WrappingIndent: standaloneEnums.WrappingIndent,
OverviewRulerLane: standaloneEnums.OverviewRulerLane,
MinimapPosition: standaloneEnums.MinimapPosition,
EndOfLinePreference: standaloneEnums.EndOfLinePreference,
@@ -357,13 +355,10 @@ export function createMonacoEditorAPI(): typeof monaco.editor {
TrackedRangeStickiness: standaloneEnums.TrackedRangeStickiness,
CursorChangeReason: standaloneEnums.CursorChangeReason,
MouseTargetType: standaloneEnums.MouseTargetType,
TextEditorCursorStyle: standaloneEnums.TextEditorCursorStyle,
TextEditorCursorBlinkingStyle: standaloneEnums.TextEditorCursorBlinkingStyle,
ContentWidgetPositionPreference: standaloneEnums.ContentWidgetPositionPreference,
OverlayWidgetPositionPreference: standaloneEnums.OverlayWidgetPositionPreference,
RenderMinimap: standaloneEnums.RenderMinimap,
ScrollType: standaloneEnums.ScrollType,
RenderLineNumbersType: standaloneEnums.RenderLineNumbersType,
// classes
ConfigurationChangedEvent: <any>ConfigurationChangedEvent,
@@ -12,7 +12,7 @@ import { IModeService } from 'vs/editor/common/services/modeService';
import { ModeServiceImpl } from 'vs/editor/common/services/modeServiceImpl';
import { IModelService } from 'vs/editor/common/services/modelService';
import { ModelServiceImpl } from 'vs/editor/common/services/modelServiceImpl';
import { ITextResourceConfigurationService, ITextResourcePropertiesService } from 'vs/editor/common/services/resourceConfiguration';
import { IResourceConfigurationService, ITextResourcePropertiesService } from 'vs/editor/common/services/resourceConfiguration';
import { SimpleBulkEditService, SimpleConfigurationService, SimpleDialogService, SimpleNotificationService, SimpleEditorProgressService, SimpleResourceConfigurationService, SimpleResourcePropertiesService, SimpleUriLabelService, SimpleWorkspaceContextService, StandaloneCommandService, StandaloneKeybindingService, StandaloneTelemetryService, SimpleLayoutService } from 'vs/editor/standalone/browser/simpleServices';
import { StandaloneCodeEditorServiceImpl } from 'vs/editor/standalone/browser/standaloneCodeServiceImpl';
import { StandaloneThemeServiceImpl } from 'vs/editor/standalone/browser/standaloneThemeServiceImpl';
@@ -126,7 +126,7 @@ export module StaticServices {
const configurationServiceImpl = new SimpleConfigurationService();
export const configurationService = define(IConfigurationService, () => configurationServiceImpl);
export const resourceConfigurationService = define(ITextResourceConfigurationService, () => new SimpleResourceConfigurationService(configurationServiceImpl));
export const resourceConfigurationService = define(IResourceConfigurationService, () => new SimpleResourceConfigurationService(configurationServiceImpl));
export const resourcePropertiesService = define(ITextResourcePropertiesService, () => new SimpleResourcePropertiesService(configurationServiceImpl));
@@ -146,7 +146,9 @@ export module StaticServices {
export const standaloneThemeService = define(IStandaloneThemeService, () => new StandaloneThemeServiceImpl());
export const modelService = define(IModelService, (o) => new ModelServiceImpl(configurationService.get(o), resourcePropertiesService.get(o), standaloneThemeService.get(o)));
export const logService = define(ILogService, () => new NullLogService());
export const modelService = define(IModelService, (o) => new ModelServiceImpl(configurationService.get(o), resourcePropertiesService.get(o), standaloneThemeService.get(o), logService.get(o)));
export const markerDecorationsService = define(IMarkerDecorationsService, (o) => new MarkerDecorationsService(modelService.get(o), markerService.get(o)));
@@ -156,8 +158,6 @@ export module StaticServices {
export const storageService = define(IStorageService, () => new InMemoryStorageService());
export const logService = define(ILogService, () => new NullLogService());
export const editorWorkerService = define(IEditorWorkerService, (o) => new EditorWorkerServiceImpl(modelService.get(o), resourceConfigurationService.get(o), logService.get(o)));
}
@@ -17,6 +17,7 @@ import { ITextResourcePropertiesService } from 'vs/editor/common/services/resour
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService';
import { TestThemeService } from 'vs/platform/theme/test/common/testThemeService';
import { NullLogService } from 'vs/platform/log/common/log';
const GENERATE_TESTS = false;
@@ -28,7 +29,7 @@ suite('ModelService', () => {
configService.setUserConfiguration('files', { 'eol': '\n' });
configService.setUserConfiguration('files', { 'eol': '\r\n' }, URI.file(platform.isWindows ? 'c:\\myroot' : '/myroot'));
modelService = new ModelServiceImpl(configService, new TestTextResourcePropertiesService(configService), new TestThemeService());
modelService = new ModelServiceImpl(configService, new TestTextResourcePropertiesService(configService), new TestThemeService(), new NullLogService());
});
teardown(() => {
@@ -0,0 +1,301 @@
/*---------------------------------------------------------------------------------------------
* 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 { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService';
import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock';
import { IModelService } from 'vs/editor/common/services/modelService';
import { IModeService } from 'vs/editor/common/services/modeService';
import { IConfigurationValue, IConfigurationService, ConfigurationTarget } from 'vs/platform/configuration/common/configuration';
import { TextResourceConfigurationService } from 'vs/editor/common/services/resourceConfigurationImpl';
import { URI } from 'vs/base/common/uri';
suite('TextResourceConfigurationService - Update', () => {
let configurationValue: IConfigurationValue<any> = {};
let updateArgs: any[];
let configurationService = new class extends TestConfigurationService {
inspect() {
return configurationValue;
}
updateValue() {
updateArgs = [...arguments];
return Promise.resolve();
}
}();
let language: string | null = null;
let testObject: TextResourceConfigurationService;
setup(() => {
const instantiationService = new TestInstantiationService();
instantiationService.stub(IModelService, <Partial<IModelService>>{ getModel() { return null; } });
instantiationService.stub(IModeService, <Partial<IModeService>>{ getModeIdByFilepathOrFirstLine() { return language; } });
instantiationService.stub(IConfigurationService, configurationService);
testObject = instantiationService.createInstance(TextResourceConfigurationService);
});
test('updateValue writes without target and overrides when no language is defined', async () => {
const resource = URI.file('someFile');
await testObject.updateValue(resource, 'a', 'b');
assert.deepEqual(updateArgs, ['a', 'b', { resource }, ConfigurationTarget.USER_LOCAL]);
});
test('updateValue writes with target and without overrides when no language is defined', async () => {
const resource = URI.file('someFile');
await testObject.updateValue(resource, 'a', 'b', ConfigurationTarget.USER_LOCAL);
assert.deepEqual(updateArgs, ['a', 'b', { resource }, ConfigurationTarget.USER_LOCAL]);
});
test('updateValue writes into given memory target without overrides', async () => {
language = 'a';
configurationValue = {
defaultTarget: { value: '1' },
userLocalTarget: { value: '2' },
workspaceFolderTarget: { value: '1' },
};
const resource = URI.file('someFile');
await testObject.updateValue(resource, 'a', 'b', ConfigurationTarget.MEMORY);
assert.deepEqual(updateArgs, ['a', 'b', { resource }, ConfigurationTarget.MEMORY]);
});
test('updateValue writes into given workspace target without overrides', async () => {
language = 'a';
configurationValue = {
defaultTarget: { value: '1' },
userLocalTarget: { value: '2' },
workspaceFolderTarget: { value: '2' },
};
const resource = URI.file('someFile');
await testObject.updateValue(resource, 'a', 'b', ConfigurationTarget.WORKSPACE);
assert.deepEqual(updateArgs, ['a', 'b', { resource }, ConfigurationTarget.WORKSPACE]);
});
test('updateValue writes into given user target without overrides', async () => {
language = 'a';
configurationValue = {
defaultTarget: { value: '1' },
userLocalTarget: { value: '2' },
workspaceFolderTarget: { value: '2' },
};
const resource = URI.file('someFile');
await testObject.updateValue(resource, 'a', 'b', ConfigurationTarget.USER);
assert.deepEqual(updateArgs, ['a', 'b', { resource }, ConfigurationTarget.USER]);
});
test('updateValue writes into given workspace folder target with overrides', async () => {
language = 'a';
configurationValue = {
defaultTarget: { value: '1' },
userLocalTarget: { value: '2' },
workspaceFolderTarget: { value: '2', override: '1' },
};
const resource = URI.file('someFile');
await testObject.updateValue(resource, 'a', 'b', ConfigurationTarget.WORKSPACE_FOLDER);
assert.deepEqual(updateArgs, ['a', 'b', { resource, overrideIdentifier: language }, ConfigurationTarget.WORKSPACE_FOLDER]);
});
test('updateValue writes into derived workspace folder target without overrides', async () => {
language = 'a';
configurationValue = {
defaultTarget: { value: '1' },
userLocalTarget: { value: '2' },
workspaceFolderTarget: { value: '2' },
};
const resource = URI.file('someFile');
await testObject.updateValue(resource, 'a', 'b');
assert.deepEqual(updateArgs, ['a', 'b', { resource }, ConfigurationTarget.WORKSPACE_FOLDER]);
});
test('updateValue writes into derived workspace folder target with overrides', async () => {
language = 'a';
configurationValue = {
defaultTarget: { value: '1' },
userLocalTarget: { value: '2' },
workspaceTarget: { value: '2', override: '1' },
workspaceFolderTarget: { value: '2', override: '2' },
};
const resource = URI.file('someFile');
await testObject.updateValue(resource, 'a', 'b');
assert.deepEqual(updateArgs, ['a', 'b', { resource, overrideIdentifier: language }, ConfigurationTarget.WORKSPACE_FOLDER]);
});
test('updateValue writes into derived workspace target without overrides', async () => {
language = 'a';
configurationValue = {
defaultTarget: { value: '1' },
userLocalTarget: { value: '2' },
workspaceTarget: { value: '2' },
};
const resource = URI.file('someFile');
await testObject.updateValue(resource, 'a', 'b');
assert.deepEqual(updateArgs, ['a', 'b', { resource }, ConfigurationTarget.WORKSPACE]);
});
test('updateValue writes into derived workspace target with overrides', async () => {
language = 'a';
configurationValue = {
defaultTarget: { value: '1' },
userLocalTarget: { value: '2' },
workspaceTarget: { value: '2', override: '2' },
};
const resource = URI.file('someFile');
await testObject.updateValue(resource, 'a', 'b');
assert.deepEqual(updateArgs, ['a', 'b', { resource, overrideIdentifier: language }, ConfigurationTarget.WORKSPACE]);
});
test('updateValue writes into derived workspace target with overrides and value defined in folder', async () => {
language = 'a';
configurationValue = {
defaultTarget: { value: '1', override: '3' },
userLocalTarget: { value: '2' },
workspaceTarget: { value: '2', override: '2' },
workspaceFolderTarget: { value: '2' },
};
const resource = URI.file('someFile');
await testObject.updateValue(resource, 'a', 'b');
assert.deepEqual(updateArgs, ['a', 'b', { resource, overrideIdentifier: language }, ConfigurationTarget.WORKSPACE]);
});
test('updateValue writes into derived user remote target without overrides', async () => {
language = 'a';
configurationValue = {
defaultTarget: { value: '1' },
userLocalTarget: { value: '2' },
userRemoteTarget: { value: '2' },
};
const resource = URI.file('someFile');
await testObject.updateValue(resource, 'a', 'b');
assert.deepEqual(updateArgs, ['a', 'b', { resource }, ConfigurationTarget.USER_REMOTE]);
});
test('updateValue writes into derived user remote target with overrides', async () => {
language = 'a';
configurationValue = {
defaultTarget: { value: '1' },
userLocalTarget: { value: '2' },
userRemoteTarget: { value: '2', override: '3' },
};
const resource = URI.file('someFile');
await testObject.updateValue(resource, 'a', 'b');
assert.deepEqual(updateArgs, ['a', 'b', { resource, overrideIdentifier: language }, ConfigurationTarget.USER_REMOTE]);
});
test('updateValue writes into derived user remote target with overrides and value defined in workspace', async () => {
language = 'a';
configurationValue = {
defaultTarget: { value: '1' },
userLocalTarget: { value: '2' },
userRemoteTarget: { value: '2', override: '3' },
workspaceTarget: { value: '3' }
};
const resource = URI.file('someFile');
await testObject.updateValue(resource, 'a', 'b');
assert.deepEqual(updateArgs, ['a', 'b', { resource, overrideIdentifier: language }, ConfigurationTarget.USER_REMOTE]);
});
test('updateValue writes into derived user remote target with overrides and value defined in workspace folder', async () => {
language = 'a';
configurationValue = {
defaultTarget: { value: '1' },
userLocalTarget: { value: '2', override: '1' },
userRemoteTarget: { value: '2', override: '3' },
workspaceTarget: { value: '3' },
workspaceFolderTarget: { value: '3' }
};
const resource = URI.file('someFile');
await testObject.updateValue(resource, 'a', 'b');
assert.deepEqual(updateArgs, ['a', 'b', { resource, overrideIdentifier: language }, ConfigurationTarget.USER_REMOTE]);
});
test('updateValue writes into derived user target without overrides', async () => {
language = 'a';
configurationValue = {
defaultTarget: { value: '1' },
userLocalTarget: { value: '2' },
};
const resource = URI.file('someFile');
await testObject.updateValue(resource, 'a', 'b');
assert.deepEqual(updateArgs, ['a', 'b', { resource }, ConfigurationTarget.USER_LOCAL]);
});
test('updateValue writes into derived user target with overrides', async () => {
language = 'a';
configurationValue = {
defaultTarget: { value: '1' },
userLocalTarget: { value: '2', override: '3' },
};
const resource = URI.file('someFile');
await testObject.updateValue(resource, 'a', '2');
assert.deepEqual(updateArgs, ['a', '2', { resource, overrideIdentifier: language }, ConfigurationTarget.USER_LOCAL]);
});
test('updateValue writes into derived user target with overrides and value is defined in remote', async () => {
language = 'a';
configurationValue = {
defaultTarget: { value: '1' },
userLocalTarget: { value: '2', override: '3' },
userRemoteTarget: { value: '3' }
};
const resource = URI.file('someFile');
await testObject.updateValue(resource, 'a', '2');
assert.deepEqual(updateArgs, ['a', '2', { resource, overrideIdentifier: language }, ConfigurationTarget.USER_LOCAL]);
});
test('updateValue writes into derived user target with overrides and value is defined in workspace', async () => {
language = 'a';
configurationValue = {
defaultTarget: { value: '1' },
userLocalTarget: { value: '2', override: '3' },
workspace: { value: '3' }
};
const resource = URI.file('someFile');
await testObject.updateValue(resource, 'a', '2');
assert.deepEqual(updateArgs, ['a', '2', { resource, overrideIdentifier: language }, ConfigurationTarget.USER_LOCAL]);
});
test('updateValue writes into derived user target with overrides and value is defined in workspace folder', async () => {
language = 'a';
configurationValue = {
defaultTarget: { value: '1', override: '3' },
userLocalTarget: { value: '2', override: '3' },
userRemoteTarget: { value: '3' },
workspaceFolder: { value: '3' }
};
const resource = URI.file('someFile');
await testObject.updateValue(resource, 'a', '2');
assert.deepEqual(updateArgs, ['a', '2', { resource, overrideIdentifier: language }, ConfigurationTarget.USER_LOCAL]);
});
test('updateValue when not changed', async () => {
language = 'a';
configurationValue = {
defaultTarget: { value: '1' },
};
const resource = URI.file('someFile');
await testObject.updateValue(resource, 'a', 'b');
assert.deepEqual(updateArgs, ['a', 'b', { resource }, ConfigurationTarget.USER_LOCAL]);
});
});
-142
View File
@@ -2419,15 +2419,6 @@ declare namespace monaco.editor {
readonly reason: CursorChangeReason;
}
export enum AccessibilitySupport {
/**
* This should be the browser case where it is not known if a screen reader is attached or no.
*/
Unknown = 0,
Disabled = 1,
Enabled = 2
}
/**
* Configuration options for auto closing quotes and brackets
*/
@@ -2992,66 +2983,6 @@ declare namespace monaco.editor {
export class ConfigurationChangedEvent {
}
/**
* The kind of animation in which the editor's cursor should be rendered.
*/
export enum TextEditorCursorBlinkingStyle {
/**
* Hidden
*/
Hidden = 0,
/**
* Blinking
*/
Blink = 1,
/**
* Blinking with smooth fading
*/
Smooth = 2,
/**
* Blinking with prolonged filled state and smooth fading
*/
Phase = 3,
/**
* Expand collapse animation on the y axis
*/
Expand = 4,
/**
* No-Blinking
*/
Solid = 5
}
/**
* The style in which the editor's cursor should be rendered.
*/
export enum TextEditorCursorStyle {
/**
* As a vertical line (sitting between two characters).
*/
Line = 1,
/**
* As a block (sitting on top of a character).
*/
Block = 2,
/**
* As a horizontal line (sitting under a character).
*/
Underline = 3,
/**
* As a thin vertical line (sitting between two characters).
*/
LineThin = 4,
/**
* As an outlined block (sitting on top of a character).
*/
BlockOutline = 5,
/**
* As a thin horizontal line (sitting under a character).
*/
UnderlineThin = 6
}
/**
* Configuration options for editor find widget
*/
@@ -3067,8 +2998,6 @@ declare namespace monaco.editor {
addExtraSpaceOnTop?: boolean;
}
export type EditorFindOptions = Readonly<Required<IEditorFindOptions>>;
export type GoToLocationValues = 'peek' | 'gotoAndPeek' | 'goto';
/**
@@ -3088,8 +3017,6 @@ declare namespace monaco.editor {
alternativeReferenceCommand?: string;
}
export type GoToLocationOptions = Readonly<Required<IGotoLocationOptions>>;
/**
* Configuration options for editor hover
*/
@@ -3111,8 +3038,6 @@ declare namespace monaco.editor {
sticky?: boolean;
}
export type EditorHoverOptions = Readonly<Required<IEditorHoverOptions>>;
/**
* A description for the overview ruler position.
*/
@@ -3242,8 +3167,6 @@ declare namespace monaco.editor {
enabled?: boolean;
}
export type EditorLightbulbOptions = Readonly<Required<IEditorLightbulbOptions>>;
/**
* Configuration options for editor minimap
*/
@@ -3279,8 +3202,6 @@ declare namespace monaco.editor {
scale?: number;
}
export type EditorMinimapOptions = Readonly<Required<IEditorMinimapOptions>>;
/**
* Configuration options for parameter hints
*/
@@ -3297,8 +3218,6 @@ declare namespace monaco.editor {
cycle?: boolean;
}
export type InternalParameterHintOptions = Readonly<Required<IEditorParameterHintOptions>>;
/**
* Configuration options for quick suggestions
*/
@@ -3308,23 +3227,8 @@ declare namespace monaco.editor {
strings: boolean;
}
export type ValidQuickSuggestionsOptions = boolean | Readonly<Required<IQuickSuggestionsOptions>>;
export type LineNumbersType = 'on' | 'off' | 'relative' | 'interval' | ((lineNumber: number) => string);
export enum RenderLineNumbersType {
Off = 0,
On = 1,
Relative = 2,
Interval = 3,
Custom = 4
}
export interface InternalEditorRenderLineNumbersOptions {
readonly renderType: RenderLineNumbersType;
readonly renderFn: ((lineNumber: number) => string) | null;
}
/**
* Configuration options for editor scrollbars
*/
@@ -3391,21 +3295,6 @@ declare namespace monaco.editor {
horizontalSliderSize?: number;
}
export interface InternalEditorScrollbarOptions {
readonly arrowSize: number;
readonly vertical: ScrollbarVisibility;
readonly horizontal: ScrollbarVisibility;
readonly useShadows: boolean;
readonly verticalHasArrows: boolean;
readonly horizontalHasArrows: boolean;
readonly handleMouseWheel: boolean;
readonly alwaysConsumeMouseWheel: boolean;
readonly horizontalScrollbarSize: number;
readonly horizontalSliderSize: number;
readonly verticalScrollbarSize: number;
readonly verticalSliderSize: number;
}
/**
* Configuration options for editor suggest widget
*/
@@ -3544,37 +3433,6 @@ declare namespace monaco.editor {
showSnippets?: boolean;
}
export type InternalSuggestOptions = Readonly<Required<ISuggestOptions>>;
/**
* Describes how to indent wrapped lines.
*/
export enum WrappingIndent {
/**
* No indentation => wrapped lines begin at column 1.
*/
None = 0,
/**
* Same => wrapped lines get the same indentation as the parent.
*/
Same = 1,
/**
* Indent => wrapped lines get +1 indentation toward the parent.
*/
Indent = 2,
/**
* DeepIndent => wrapped lines get +2 indentation toward the parent.
*/
DeepIndent = 3
}
export interface EditorWrappingInfo {
readonly isDominatedByLongLines: boolean;
readonly isWordWrapMinified: boolean;
readonly isViewportWrapping: boolean;
readonly wrappingColumn: number;
}
/**
* A view zone is a full horizontal rectangle that 'pushes' text down.
* The editor reserves space for view zones when rendering.
@@ -11,7 +11,7 @@ import { Registry } from 'vs/platform/registry/common/platform';
import { IWorkspaceFolder } from 'vs/platform/workspace/common/workspace';
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
import { IConfigurationRegistry, Extensions, OVERRIDE_PROPERTY_PATTERN } from 'vs/platform/configuration/common/configurationRegistry';
import { ResourceMap } from 'vs/base/common/map';
import { IStringDictionary } from 'vs/base/common/collections';
export const IConfigurationService = createDecorator<IConfigurationService>('configurationService');
@@ -48,18 +48,41 @@ export function ConfigurationTargetToString(configurationTarget: ConfigurationTa
}
}
export interface IConfigurationChange {
keys: string[];
overrides: [string, string[]][];
}
export interface IConfigurationChangeEvent {
source: ConfigurationTarget;
affectedKeys: string[];
affectsConfiguration(configuration: string, resource?: URI): boolean;
readonly source: ConfigurationTarget;
readonly affectedKeys: string[];
readonly change: IConfigurationChange;
affectsConfiguration(configuration: string, overrides?: IConfigurationOverrides): boolean;
// Following data is used for telemetry
sourceConfig: any;
readonly sourceConfig: any;
}
// Following data is used for Extension host configuration event
changedConfiguration: IConfigurationModel;
changedConfigurationByResource: ResourceMap<IConfigurationModel>;
export interface IConfigurationValue<T> {
readonly default?: T;
readonly user?: T;
readonly userLocal?: T;
readonly userRemote?: T;
readonly workspace?: T;
readonly workspaceFolder?: T;
readonly memory?: T;
readonly value?: T;
readonly defaultTarget?: { value?: T, override?: T };
readonly userTarget?: { value?: T, override?: T };
readonly userLocalTarget?: { value?: T, override?: T };
readonly userRemoteTarget?: { value?: T, override?: T };
readonly workspaceTarget?: { value?: T, override?: T };
readonly workspaceFolderTarget?: { value?: T, override?: T };
readonly memoryTarget?: { value?: T, override?: T };
}
export interface IConfigurationService {
@@ -87,18 +110,9 @@ export interface IConfigurationService {
updateValue(key: string, value: any, target: ConfigurationTarget): Promise<void>;
updateValue(key: string, value: any, overrides: IConfigurationOverrides, target: ConfigurationTarget, donotNotifyError?: boolean): Promise<void>;
reloadConfiguration(folder?: IWorkspaceFolder): Promise<void>;
inspect<T>(key: string, overrides?: IConfigurationOverrides): IConfigurationValue<T>;
inspect<T>(key: string, overrides?: IConfigurationOverrides): {
default: T,
user: T,
userLocal?: T,
userRemote?: T,
workspace?: T,
workspaceFolder?: T,
memory?: T,
value: T,
};
reloadConfiguration(folder?: IWorkspaceFolder): Promise<void>;
keys(): {
default: string[];
@@ -116,6 +130,7 @@ export interface IConfigurationModel {
}
export interface IOverrides {
keys: string[];
contents: any;
identifiers: string[];
}
@@ -127,20 +142,76 @@ export interface IConfigurationData {
folders: [UriComponents, IConfigurationModel][];
}
export function compare(from: IConfigurationModel, to: IConfigurationModel): { added: string[], removed: string[], updated: string[] } {
const added = to.keys.filter(key => from.keys.indexOf(key) === -1);
const removed = from.keys.filter(key => to.keys.indexOf(key) === -1);
export interface IConfigurationCompareResult {
added: string[];
removed: string[];
updated: string[];
overrides: [string, string[]][];
}
export function compare(from: IConfigurationModel | undefined, to: IConfigurationModel | undefined): IConfigurationCompareResult {
const added = to
? from ? to.keys.filter(key => from.keys.indexOf(key) === -1) : [...to.keys]
: [];
const removed = from
? to ? from.keys.filter(key => to.keys.indexOf(key) === -1) : [...from.keys]
: [];
const updated: string[] = [];
for (const key of from.keys) {
const value1 = getConfigurationValue(from.contents, key);
const value2 = getConfigurationValue(to.contents, key);
if (!objects.equals(value1, value2)) {
updated.push(key);
if (to && from) {
for (const key of from.keys) {
if (to.keys.indexOf(key) !== -1) {
const value1 = getConfigurationValue(from.contents, key);
const value2 = getConfigurationValue(to.contents, key);
if (!objects.equals(value1, value2)) {
updated.push(key);
}
}
}
}
return { added, removed, updated };
const overrides: [string, string[]][] = [];
const byOverrideIdentifier = (overrides: IOverrides[]): IStringDictionary<IOverrides> => {
const result: IStringDictionary<IOverrides> = {};
for (const override of overrides) {
for (const identifier of override.identifiers) {
result[keyFromOverrideIdentifier(identifier)] = override;
}
}
return result;
};
const toOverridesByIdentifier: IStringDictionary<IOverrides> = to ? byOverrideIdentifier(to.overrides) : {};
const fromOverridesByIdentifier: IStringDictionary<IOverrides> = from ? byOverrideIdentifier(from.overrides) : {};
if (Object.keys(toOverridesByIdentifier).length) {
for (const key of added) {
const override = toOverridesByIdentifier[key];
if (override) {
overrides.push([overrideIdentifierFromKey(key), override.keys]);
}
}
}
if (Object.keys(fromOverridesByIdentifier).length) {
for (const key of removed) {
const override = fromOverridesByIdentifier[key];
if (override) {
overrides.push([overrideIdentifierFromKey(key), override.keys]);
}
}
}
if (Object.keys(toOverridesByIdentifier).length && Object.keys(fromOverridesByIdentifier).length) {
for (const key of updated) {
const fromOverride = fromOverridesByIdentifier[key];
const toOverride = toOverridesByIdentifier[key];
if (fromOverride && toOverride) {
const result = compare({ contents: fromOverride.contents, keys: fromOverride.keys, overrides: [] }, { contents: toOverride.contents, keys: toOverride.keys, overrides: [] });
overrides.push([overrideIdentifierFromKey(key), [...result.added, ...result.removed, ...result.updated]]);
}
}
}
return { added, removed, updated, overrides };
}
export function toOverrides(raw: any, conflictReporter: (message: string) => void): IOverrides[] {
@@ -156,6 +227,7 @@ export function toOverrides(raw: any, conflictReporter: (message: string) => voi
}
overrides.push({
identifiers: [overrideIdentifierFromKey(key).trim()],
keys: Object.keys(overrideRaw),
contents: toValuesTree(overrideRaw, conflictReporter)
});
}
@@ -290,10 +362,10 @@ export function getMigratedSettingValue<T>(configurationService: IConfigurationS
const legacySetting = configurationService.inspect<T>(legacySettingName);
if (typeof setting.user !== 'undefined' || typeof setting.workspace !== 'undefined' || typeof setting.workspaceFolder !== 'undefined') {
return setting.value;
return setting.value!;
} else if (typeof legacySetting.user !== 'undefined' || typeof legacySetting.workspace !== 'undefined' || typeof legacySetting.workspaceFolder !== 'undefined') {
return legacySetting.value;
return legacySetting.value!;
} else {
return setting.default;
return setting.default!;
}
}
@@ -4,13 +4,13 @@
*--------------------------------------------------------------------------------------------*/
import * as json from 'vs/base/common/json';
import { ResourceMap } from 'vs/base/common/map';
import { ResourceMap, values, getOrSet } from 'vs/base/common/map';
import * as arrays from 'vs/base/common/arrays';
import * as types from 'vs/base/common/types';
import * as objects from 'vs/base/common/objects';
import { URI, UriComponents } from 'vs/base/common/uri';
import { OVERRIDE_PROPERTY_PATTERN, ConfigurationScope, IConfigurationRegistry, Extensions, IConfigurationPropertySchema } from 'vs/platform/configuration/common/configurationRegistry';
import { IOverrides, overrideIdentifierFromKey, addToValueTree, toValuesTree, IConfigurationModel, getConfigurationValue, IConfigurationOverrides, IConfigurationData, getDefaultValues, getConfigurationKeys, IConfigurationChangeEvent, ConfigurationTarget, removeFromValueTree, toOverrides } from 'vs/platform/configuration/common/configuration';
import { IOverrides, overrideIdentifierFromKey, addToValueTree, toValuesTree, IConfigurationModel, getConfigurationValue, IConfigurationOverrides, IConfigurationData, getDefaultValues, getConfigurationKeys, removeFromValueTree, toOverrides, IConfigurationValue, ConfigurationTarget, compare, IConfigurationChangeEvent, IConfigurationChange } from 'vs/platform/configuration/common/configuration';
import { Workspace } from 'vs/platform/workspace/common/workspace';
import { Registry } from 'vs/platform/registry/common/platform';
@@ -45,6 +45,22 @@ export class ConfigurationModel implements IConfigurationModel {
return section ? getConfigurationValue<any>(this.contents, section) : this.contents;
}
getOverrideValue<V>(section: string | undefined, overrideIdentifier: string): V | undefined {
const overrideContents = this.getContentsForOverrideIdentifer(overrideIdentifier);
return overrideContents
? section ? getConfigurationValue<any>(overrideContents, section) : overrideContents
: undefined;
}
getKeysForOverrideIdentifier(identifier: string): string[] {
for (const override of this.overrides) {
if (override.identifiers.indexOf(identifier) !== -1) {
return override.keys;
}
}
return [];
}
override(identifier: string): ConfigurationModel {
const overrideContents = this.getContentsForOverrideIdentifer(identifier);
@@ -186,7 +202,8 @@ export class DefaultConfigurationModel extends ConfigurationModel {
if (OVERRIDE_PROPERTY_PATTERN.test(key)) {
overrides.push({
identifiers: [overrideIdentifierFromKey(key).trim()],
contents: toValuesTree(contents[key], message => console.error(`Conflict in default settings file: ${message}`))
keys: Object.keys(contents[key]),
contents: toValuesTree(contents[key], message => console.error(`Conflict in default settings file: ${message}`)),
});
}
}
@@ -362,28 +379,37 @@ export class Configuration {
}
}
inspect<C>(key: string, overrides: IConfigurationOverrides, workspace: Workspace | undefined): {
default: C,
user: C,
userLocal?: C,
userRemote?: C,
workspace?: C,
workspaceFolder?: C
memory?: C
value: C,
} {
inspect<C>(key: string, overrides: IConfigurationOverrides, workspace: Workspace | undefined): IConfigurationValue<C> {
const consolidateConfigurationModel = this.getConsolidateConfigurationModel(overrides, workspace);
const folderConfigurationModel = this.getFolderConfigurationModelForResource(overrides.resource, workspace);
const memoryConfigurationModel = overrides.resource ? this._memoryConfigurationByResource.get(overrides.resource) || this._memoryConfiguration : this._memoryConfiguration;
const defaultValue = overrides.overrideIdentifier ? this._defaultConfiguration.freeze().override(overrides.overrideIdentifier).getValue<C>(key) : this._defaultConfiguration.freeze().getValue<C>(key);
const userValue = overrides.overrideIdentifier ? this.userConfiguration.freeze().override(overrides.overrideIdentifier).getValue<C>(key) : this.userConfiguration.freeze().getValue<C>(key);
const userLocalValue = overrides.overrideIdentifier ? this.localUserConfiguration.freeze().override(overrides.overrideIdentifier).getValue<C>(key) : this.localUserConfiguration.freeze().getValue<C>(key);
const userRemoteValue = overrides.overrideIdentifier ? this.remoteUserConfiguration.freeze().override(overrides.overrideIdentifier).getValue<C>(key) : this.remoteUserConfiguration.freeze().getValue<C>(key);
const workspaceValue = workspace ? overrides.overrideIdentifier ? this._workspaceConfiguration.freeze().override(overrides.overrideIdentifier).getValue<C>(key) : this._workspaceConfiguration.freeze().getValue<C>(key) : undefined; //Check on workspace exists or not because _workspaceConfiguration is never null
const workspaceFolderValue = folderConfigurationModel ? overrides.overrideIdentifier ? folderConfigurationModel.freeze().override(overrides.overrideIdentifier).getValue<C>(key) : folderConfigurationModel.freeze().getValue<C>(key) : undefined;
const memoryValue = overrides.overrideIdentifier ? memoryConfigurationModel.override(overrides.overrideIdentifier).getValue<C>(key) : memoryConfigurationModel.getValue<C>(key);
const value = consolidateConfigurationModel.getValue<C>(key);
return {
default: overrides.overrideIdentifier ? this._defaultConfiguration.freeze().override(overrides.overrideIdentifier).getValue(key) : this._defaultConfiguration.freeze().getValue(key),
user: overrides.overrideIdentifier ? this.userConfiguration.freeze().override(overrides.overrideIdentifier).getValue(key) : this.userConfiguration.freeze().getValue(key),
userLocal: overrides.overrideIdentifier ? this.localUserConfiguration.freeze().override(overrides.overrideIdentifier).getValue(key) : this.localUserConfiguration.freeze().getValue(key),
userRemote: overrides.overrideIdentifier ? this.remoteUserConfiguration.freeze().override(overrides.overrideIdentifier).getValue(key) : this.remoteUserConfiguration.freeze().getValue(key),
workspace: workspace ? overrides.overrideIdentifier ? this._workspaceConfiguration.freeze().override(overrides.overrideIdentifier).getValue(key) : this._workspaceConfiguration.freeze().getValue(key) : undefined, //Check on workspace exists or not because _workspaceConfiguration is never null
workspaceFolder: folderConfigurationModel ? overrides.overrideIdentifier ? folderConfigurationModel.freeze().override(overrides.overrideIdentifier).getValue(key) : folderConfigurationModel.freeze().getValue(key) : undefined,
memory: overrides.overrideIdentifier ? memoryConfigurationModel.override(overrides.overrideIdentifier).getValue(key) : memoryConfigurationModel.getValue(key),
value: consolidateConfigurationModel.getValue(key)
default: defaultValue,
user: userValue,
userLocal: userLocalValue,
userRemote: userRemoteValue,
workspace: workspaceValue,
workspaceFolder: workspaceFolderValue,
memory: memoryValue,
value,
defaultTarget: defaultValue !== undefined ? { value: this._defaultConfiguration.freeze().getValue(key), override: overrides.overrideIdentifier ? this._defaultConfiguration.freeze().getOverrideValue(key, overrides.overrideIdentifier) : undefined } : undefined,
userTarget: userValue !== undefined ? { value: this.userConfiguration.freeze().getValue(key), override: overrides.overrideIdentifier ? this.userConfiguration.freeze().getOverrideValue(key, overrides.overrideIdentifier) : undefined } : undefined,
userLocalTarget: userLocalValue !== undefined ? { value: this.localUserConfiguration.freeze().getValue(key), override: overrides.overrideIdentifier ? this.localUserConfiguration.freeze().getOverrideValue(key, overrides.overrideIdentifier) : undefined } : undefined,
userRemoteTarget: userRemoteValue !== undefined ? { value: this.remoteUserConfiguration.freeze().getValue(key), override: overrides.overrideIdentifier ? this.remoteUserConfiguration.freeze().getOverrideValue(key, overrides.overrideIdentifier) : undefined } : undefined,
workspaceTarget: workspaceValue !== undefined ? { value: this._workspaceConfiguration.freeze().getValue(key), override: overrides.overrideIdentifier ? this._workspaceConfiguration.freeze().getOverrideValue(key, overrides.overrideIdentifier) : undefined } : undefined,
workspaceFolderTarget: workspaceFolderValue !== undefined ? { value: folderConfigurationModel?.freeze().getValue(key), override: overrides.overrideIdentifier ? folderConfigurationModel?.freeze().getOverrideValue(key, overrides.overrideIdentifier) : undefined } : undefined,
memoryTarget: memoryValue !== undefined ? { value: memoryConfigurationModel.getValue(key), override: overrides.overrideIdentifier ? memoryConfigurationModel.getOverrideValue(key, overrides.overrideIdentifier) : undefined } : undefined,
};
}
@@ -438,6 +464,71 @@ export class Configuration {
this._foldersConsolidatedConfigurations.delete(resource);
}
compareAndUpdateDefaultConfiguration(defaults: ConfigurationModel, keys: string[]): IConfigurationChange {
const overrides: [string, string[]][] = keys
.filter(key => OVERRIDE_PROPERTY_PATTERN.test(key))
.map(key => {
const overrideIdentifier = overrideIdentifierFromKey(key);
const fromKeys = this._defaultConfiguration.getKeysForOverrideIdentifier(overrideIdentifier);
const toKeys = defaults.getKeysForOverrideIdentifier(overrideIdentifier);
const keys = [
...toKeys.filter(key => fromKeys.indexOf(key) === -1),
...fromKeys.filter(key => toKeys.indexOf(key) === -1),
...fromKeys.filter(key => !objects.equals(this._defaultConfiguration.override(overrideIdentifier).getValue(key), defaults.override(overrideIdentifier).getValue(key)))
];
return [overrideIdentifier, keys];
});
this.updateDefaultConfiguration(defaults);
return { keys, overrides };
}
compareAndUpdateLocalUserConfiguration(user: ConfigurationModel): IConfigurationChange {
const { added, updated, removed, overrides } = compare(this.localUserConfiguration, user);
const keys = [...added, ...updated, ...removed];
if (keys.length) {
this.updateLocalUserConfiguration(user);
}
return { keys, overrides };
}
compareAndUpdateRemoteUserConfiguration(user: ConfigurationModel): IConfigurationChange {
const { added, updated, removed, overrides } = compare(this.remoteUserConfiguration, user);
let keys = [...added, ...updated, ...removed];
if (keys.length) {
this.updateRemoteUserConfiguration(user);
}
return { keys, overrides };
}
compareAndUpdateWorkspaceConfiguration(workspaceConfiguration: ConfigurationModel): IConfigurationChange {
const { added, updated, removed, overrides } = compare(this.workspaceConfiguration, workspaceConfiguration);
let keys = [...added, ...updated, ...removed];
if (keys.length) {
this.updateWorkspaceConfiguration(workspaceConfiguration);
}
return { keys, overrides };
}
compareAndUpdateFolderConfiguration(resource: URI, folderConfiguration: ConfigurationModel): IConfigurationChange {
const currentFolderConfiguration = this.folderConfigurations.get(resource);
const { added, updated, removed, overrides } = compare(currentFolderConfiguration, folderConfiguration);
let keys = [...added, ...updated, ...removed];
if (keys.length) {
this.updateFolderConfiguration(resource, folderConfiguration);
}
return { keys, overrides };
}
compareAndDeleteFolderConfiguration(folder: URI): IConfigurationChange {
const folderConfig = this.folderConfigurations.get(folder);
if (!folderConfig) {
throw new Error('Unknown folder');
}
this.deleteFolderConfiguration(folder);
const { added, updated, removed, overrides } = compare(folderConfig, undefined);
return { keys: [...added, ...updated, ...removed], overrides };
}
get defaults(): ConfigurationModel {
return this._defaultConfiguration;
}
@@ -554,131 +645,118 @@ export class Configuration {
};
}
allKeys(workspace: Workspace | undefined): string[] {
let keys = this.keys(workspace);
let all = [...keys.default];
const addKeys = (keys: string[]) => {
for (const key of keys) {
if (all.indexOf(key) === -1) {
all.push(key);
}
}
};
addKeys(keys.user);
addKeys(keys.workspace);
for (const resource of this.folderConfigurations.keys()) {
addKeys(this.folderConfigurations.get(resource)!.keys);
}
return all;
allKeys(): string[] {
const keys: Set<string> = new Set<string>();
this._defaultConfiguration.freeze().keys.forEach(key => keys.add(key));
this.userConfiguration.freeze().keys.forEach(key => keys.add(key));
this._workspaceConfiguration.freeze().keys.forEach(key => keys.add(key));
this._folderConfigurations.forEach(folderConfiguraiton => folderConfiguraiton.freeze().keys.forEach(key => keys.add(key)));
return values(keys);
}
protected getAllKeysForOverrideIdentifier(overrideIdentifier: string): string[] {
const keys: Set<string> = new Set<string>();
this._defaultConfiguration.getKeysForOverrideIdentifier(overrideIdentifier).forEach(key => keys.add(key));
this.userConfiguration.getKeysForOverrideIdentifier(overrideIdentifier).forEach(key => keys.add(key));
this._workspaceConfiguration.getKeysForOverrideIdentifier(overrideIdentifier).forEach(key => keys.add(key));
this._folderConfigurations.forEach(folderConfiguraiton => folderConfiguraiton.getKeysForOverrideIdentifier(overrideIdentifier).forEach(key => keys.add(key)));
return values(keys);
}
static parse(data: IConfigurationData): Configuration {
const defaultConfiguration = this.parseConfigurationModel(data.defaults);
const userConfiguration = this.parseConfigurationModel(data.user);
const workspaceConfiguration = this.parseConfigurationModel(data.workspace);
const folders: ResourceMap<ConfigurationModel> = data.folders.reduce((result, value) => {
result.set(URI.revive(value[0]), this.parseConfigurationModel(value[1]));
return result;
}, new ResourceMap<ConfigurationModel>());
return new Configuration(defaultConfiguration, userConfiguration, new ConfigurationModel(), workspaceConfiguration, folders, new ConfigurationModel(), new ResourceMap<ConfigurationModel>(), false);
}
private static parseConfigurationModel(model: IConfigurationModel): ConfigurationModel {
return new ConfigurationModel(model.contents, model.keys, model.overrides).freeze();
}
}
export class AbstractConfigurationChangeEvent {
export function mergeChanges(...changes: IConfigurationChange[]): IConfigurationChange {
if (changes.length === 0) {
return { keys: [], overrides: [] };
}
if (changes.length === 1) {
return changes[0];
}
const keysSet = new Set<string>();
const overridesMap = new Map<string, Set<string>>();
for (const change of changes) {
change.keys.forEach(key => keysSet.add(key));
change.overrides.forEach(([identifier, keys]) => {
const result = getOrSet(overridesMap, identifier, new Set<string>());
keys.forEach(key => result.add(key));
});
}
const overrides: [string, string[]][] = [];
overridesMap.forEach((keys, identifier) => overrides.push([identifier, values(keys)]));
return { keys: values(keysSet), overrides };
}
protected doesConfigurationContains(configuration: ConfigurationModel, config: string): boolean {
let changedKeysTree = configuration.contents;
let requestedTree = toValuesTree({ [config]: true }, () => { });
export class ConfigurationChangeEvent implements IConfigurationChangeEvent {
private readonly affectedKeysTree: any;
readonly affectedKeys: string[];
source!: ConfigurationTarget;
sourceConfig: any;
constructor(readonly change: IConfigurationChange, private readonly previous: { workspace?: Workspace, data: IConfigurationData } | undefined, private readonly currentConfiguraiton: Configuration, private readonly currentWorkspace?: Workspace) {
const keysSet = new Set<string>();
change.keys.forEach(key => keysSet.add(key));
change.overrides.forEach(([, keys]) => keys.forEach(key => keysSet.add(key)));
this.affectedKeys = values(keysSet);
const configurationModel = new ConfigurationModel();
this.affectedKeys.forEach(key => configurationModel.setValue(key, {}));
this.affectedKeysTree = configurationModel.contents;
}
private _previousConfiguration: Configuration | undefined = undefined;
get previousConfiguration(): Configuration | undefined {
if (!this._previousConfiguration && this.previous) {
this._previousConfiguration = Configuration.parse(this.previous.data);
}
return this._previousConfiguration;
}
affectsConfiguration(section: string, overrides?: IConfigurationOverrides): boolean {
if (this.doesAffectedKeysTreeContains(this.affectedKeysTree, section)) {
if (overrides) {
const value1 = this.previousConfiguration ? this.previousConfiguration.getValue(section, overrides, this.previous?.workspace) : undefined;
const value2 = this.currentConfiguraiton.getValue(section, overrides, this.currentWorkspace);
return !objects.equals(value1, value2);
}
return true;
}
return false;
}
private doesAffectedKeysTreeContains(affectedKeysTree: any, section: string): boolean {
let requestedTree = toValuesTree({ [section]: true }, () => { });
let key;
while (typeof requestedTree === 'object' && (key = Object.keys(requestedTree)[0])) { // Only one key should present, since we added only one property
changedKeysTree = changedKeysTree[key];
if (!changedKeysTree) {
affectedKeysTree = affectedKeysTree[key];
if (!affectedKeysTree) {
return false; // Requested tree is not found
}
requestedTree = requestedTree[key];
}
return true;
}
protected updateKeys(configuration: ConfigurationModel, keys: string[], resource?: URI): void {
for (const key of keys) {
configuration.setValue(key, {});
}
}
}
export class ConfigurationChangeEvent extends AbstractConfigurationChangeEvent implements IConfigurationChangeEvent {
private _source: ConfigurationTarget;
private _sourceConfig: any;
constructor(
private _changedConfiguration: ConfigurationModel = new ConfigurationModel(),
private _changedConfigurationByResource: ResourceMap<ConfigurationModel> = new ResourceMap<ConfigurationModel>()) {
super();
this._source = ConfigurationTarget.DEFAULT;
export class AllKeysConfigurationChangeEvent extends ConfigurationChangeEvent {
constructor(configuration: Configuration, workspace: Workspace, public source: ConfigurationTarget, public sourceConfig: any) {
super({ keys: configuration.allKeys(), overrides: [] }, undefined, configuration, workspace);
}
get changedConfiguration(): IConfigurationModel {
return this._changedConfiguration;
}
get changedConfigurationByResource(): ResourceMap<IConfigurationModel> {
return this._changedConfigurationByResource;
}
change(event: ConfigurationChangeEvent): ConfigurationChangeEvent;
change(keys: string[], resource?: URI): ConfigurationChangeEvent;
change(arg1: any, arg2?: any): ConfigurationChangeEvent {
if (arg1 instanceof ConfigurationChangeEvent) {
this._changedConfiguration = this._changedConfiguration.merge(arg1._changedConfiguration);
for (const resource of arg1._changedConfigurationByResource.keys()) {
let changedConfigurationByResource = this.getOrSetChangedConfigurationForResource(resource);
changedConfigurationByResource = changedConfigurationByResource.merge(arg1._changedConfigurationByResource.get(resource)!);
this._changedConfigurationByResource.set(resource, changedConfigurationByResource);
}
} else {
this.changeWithKeys(arg1, arg2);
}
return this;
}
telemetryData(source: ConfigurationTarget, sourceConfig: any): ConfigurationChangeEvent {
this._source = source;
this._sourceConfig = sourceConfig;
return this;
}
get affectedKeys(): string[] {
const keys = [...this._changedConfiguration.keys];
this._changedConfigurationByResource.forEach(model => keys.push(...model.keys));
return arrays.distinct(keys);
}
get source(): ConfigurationTarget {
return this._source;
}
get sourceConfig(): any {
return this._sourceConfig;
}
affectsConfiguration(config: string, resource?: URI): boolean {
let configurationModelsToSearch: ConfigurationModel[] = [this._changedConfiguration];
if (resource) {
let model = this._changedConfigurationByResource.get(resource);
if (model) {
configurationModelsToSearch.push(model);
}
} else {
configurationModelsToSearch.push(...this._changedConfigurationByResource.values());
}
return configurationModelsToSearch.some(configuration => this.doesConfigurationContains(configuration, config));
}
private changeWithKeys(keys: string[], resource?: URI): void {
let changedConfiguration = resource ? this.getOrSetChangedConfigurationForResource(resource) : this._changedConfiguration;
this.updateKeys(changedConfiguration, keys);
}
private getOrSetChangedConfigurationForResource(resource: URI): ConfigurationModel {
let changedConfigurationByResource = this._changedConfigurationByResource.get(resource);
if (!changedConfigurationByResource) {
changedConfigurationByResource = new ConfigurationModel();
this._changedConfigurationByResource.set(resource, changedConfigurationByResource);
}
return changedConfigurationByResource;
}
}
@@ -6,8 +6,8 @@
import { Registry } from 'vs/platform/registry/common/platform';
import { IConfigurationRegistry, Extensions } from 'vs/platform/configuration/common/configurationRegistry';
import { IDisposable, Disposable } from 'vs/base/common/lifecycle';
import { IConfigurationService, IConfigurationChangeEvent, IConfigurationOverrides, ConfigurationTarget, compare, isConfigurationOverrides, IConfigurationData } from 'vs/platform/configuration/common/configuration';
import { DefaultConfigurationModel, Configuration, ConfigurationChangeEvent, ConfigurationModel, ConfigurationModelParser } from 'vs/platform/configuration/common/configurationModels';
import { IConfigurationService, IConfigurationChangeEvent, IConfigurationOverrides, ConfigurationTarget, isConfigurationOverrides, IConfigurationData, IConfigurationValue, IConfigurationChange } from 'vs/platform/configuration/common/configuration';
import { DefaultConfigurationModel, Configuration, ConfigurationModel, ConfigurationModelParser, ConfigurationChangeEvent } from 'vs/platform/configuration/common/configurationModels';
import { Event, Emitter } from 'vs/base/common/event';
import { IWorkspaceFolder } from 'vs/platform/workspace/common/workspace';
import { ConfigWatcher } from 'vs/base/node/config';
@@ -79,13 +79,7 @@ export class ConfigurationService extends Disposable implements IConfigurationSe
return Promise.reject(new Error('not supported'));
}
inspect<T>(key: string): {
default: T,
user: T,
workspace?: T,
workspaceFolder?: T
value: T
} {
inspect<T>(key: string): IConfigurationValue<T> {
return this.configuration.inspect<T>(key, {}, undefined);
}
@@ -109,21 +103,22 @@ export class ConfigurationService extends Disposable implements IConfigurationSe
}
private onDidChangeUserConfiguration(userConfigurationModel: ConfigurationModel): void {
const { added, updated, removed } = compare(this.configuration.localUserConfiguration, userConfigurationModel);
const changedKeys = [...added, ...updated, ...removed];
if (changedKeys.length) {
this.configuration.updateLocalUserConfiguration(userConfigurationModel);
this.trigger(changedKeys, ConfigurationTarget.USER);
}
const previous = this.configuration.toData();
const change = this.configuration.compareAndUpdateLocalUserConfiguration(userConfigurationModel);
this.trigger(change, previous, ConfigurationTarget.USER);
}
private onDidDefaultConfigurationChange(keys: string[]): void {
this.configuration.updateDefaultConfiguration(new DefaultConfigurationModel());
this.trigger(keys, ConfigurationTarget.DEFAULT);
const previous = this.configuration.toData();
const change = this.configuration.compareAndUpdateDefaultConfiguration(new DefaultConfigurationModel(), keys);
this.trigger(change, previous, ConfigurationTarget.DEFAULT);
}
private trigger(keys: string[], source: ConfigurationTarget): void {
this._onDidChangeConfiguration.fire(new ConfigurationChangeEvent().change(keys).telemetryData(source, this.getTargetConfiguration(source)));
private trigger(configurationChange: IConfigurationChange, previous: IConfigurationData, source: ConfigurationTarget): void {
const event = new ConfigurationChangeEvent(configurationChange, { data: previous }, this.configuration);
event.source = source;
event.sourceConfig = this.getTargetConfiguration(source);
this._onDidChangeConfiguration.fire(event);
}
private getTargetConfiguration(target: ConfigurationTarget): any {
@@ -135,4 +130,4 @@ export class ConfigurationService extends Disposable implements IConfigurationSe
}
return {};
}
}
}
@@ -4,6 +4,7 @@
*--------------------------------------------------------------------------------------------*/
import * as assert from 'assert';
import { merge, removeFromValueTree } from 'vs/platform/configuration/common/configuration';
import { mergeChanges } from 'vs/platform/configuration/common/configurationModels';
suite('Configuration', () => {
@@ -104,4 +105,43 @@ suite('Configuration', () => {
assert.deepEqual(target, { 'a': { 'b': { 'd': 1 } } });
});
});
});
suite('Configuration Changes: Merge', () => {
test('merge only keys', () => {
const actual = mergeChanges({ keys: ['a', 'b'], overrides: [] }, { keys: ['c', 'd'], overrides: [] });
assert.deepEqual(actual, { keys: ['a', 'b', 'c', 'd'], overrides: [] });
});
test('merge only keys with duplicates', () => {
const actual = mergeChanges({ keys: ['a', 'b'], overrides: [] }, { keys: ['c', 'd'], overrides: [] }, { keys: ['a', 'd', 'e'], overrides: [] });
assert.deepEqual(actual, { keys: ['a', 'b', 'c', 'd', 'e'], overrides: [] });
});
test('merge only overrides', () => {
const actual = mergeChanges({ keys: [], overrides: [['a', ['1', '2']]] }, { keys: [], overrides: [['b', ['3', '4']]] });
assert.deepEqual(actual, { keys: [], overrides: [['a', ['1', '2']], ['b', ['3', '4']]] });
});
test('merge only overrides with duplicates', () => {
const actual = mergeChanges({ keys: [], overrides: [['a', ['1', '2']], ['b', ['5', '4']]] }, { keys: [], overrides: [['b', ['3', '4']]] }, { keys: [], overrides: [['c', ['1', '4']], ['a', ['2', '3']]] });
assert.deepEqual(actual, { keys: [], overrides: [['a', ['1', '2', '3']], ['b', ['5', '4', '3']], ['c', ['1', '4']]] });
});
test('merge', () => {
const actual = mergeChanges({ keys: ['b', 'b'], overrides: [['a', ['1', '2']], ['b', ['5', '4']]] }, { keys: ['b'], overrides: [['b', ['3', '4']]] }, { keys: ['c', 'a'], overrides: [['c', ['1', '4']], ['a', ['2', '3']]] });
assert.deepEqual(actual, { keys: ['b', 'c', 'a'], overrides: [['a', ['1', '2', '3']], ['b', ['5', '4', '3']], ['c', ['1', '4']]] });
});
test('merge single change', () => {
const actual = mergeChanges({ keys: ['b', 'b'], overrides: [['a', ['1', '2']], ['b', ['5', '4']]] });
assert.deepEqual(actual, { keys: ['b', 'b'], overrides: [['a', ['1', '2']], ['b', ['5', '4']]] });
});
test('merge no changes', () => {
const actual = mergeChanges();
assert.deepEqual(actual, { keys: [], overrides: [] });
});
});
@@ -3,10 +3,13 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as assert from 'assert';
import { ConfigurationModel, DefaultConfigurationModel, ConfigurationChangeEvent, ConfigurationModelParser, Configuration } from 'vs/platform/configuration/common/configurationModels';
import { ConfigurationModel, DefaultConfigurationModel, ConfigurationChangeEvent, ConfigurationModelParser, Configuration, mergeChanges, AllKeysConfigurationChangeEvent } from 'vs/platform/configuration/common/configurationModels';
import { Extensions, IConfigurationRegistry } from 'vs/platform/configuration/common/configurationRegistry';
import { Registry } from 'vs/platform/registry/common/platform';
import { URI } from 'vs/base/common/uri';
import { Workspace, WorkspaceFolder } from 'vs/platform/workspace/common/workspace';
import { join } from 'vs/base/common/path';
import { ConfigurationTarget } from 'vs/platform/configuration/common/configuration';
suite('ConfigurationModel', () => {
@@ -103,7 +106,7 @@ suite('ConfigurationModel', () => {
test('get overriding configuration model for an existing identifier', () => {
let testObject = new ConfigurationModel(
{ 'a': { 'b': 1 }, 'f': 1 }, [],
[{ identifiers: ['c'], contents: { 'a': { 'd': 1 } } }]);
[{ identifiers: ['c'], contents: { 'a': { 'd': 1 } }, keys: ['a'] }]);
assert.deepEqual(testObject.override('c').contents, { 'a': { 'b': 1, 'd': 1 }, 'f': 1 });
});
@@ -111,7 +114,7 @@ suite('ConfigurationModel', () => {
test('get overriding configuration model for an identifier that does not exist', () => {
let testObject = new ConfigurationModel(
{ 'a': { 'b': 1 }, 'f': 1 }, [],
[{ identifiers: ['c'], contents: { 'a': { 'd': 1 } } }]);
[{ identifiers: ['c'], contents: { 'a': { 'd': 1 } }, keys: ['a'] }]);
assert.deepEqual(testObject.override('xyz').contents, { 'a': { 'b': 1 }, 'f': 1 });
});
@@ -119,7 +122,7 @@ suite('ConfigurationModel', () => {
test('get overriding configuration when one of the keys does not exist in base', () => {
let testObject = new ConfigurationModel(
{ 'a': { 'b': 1 }, 'f': 1 }, [],
[{ identifiers: ['c'], contents: { 'a': { 'd': 1 }, 'g': 1 } }]);
[{ identifiers: ['c'], contents: { 'a': { 'd': 1 }, 'g': 1 }, keys: ['a', 'g'] }]);
assert.deepEqual(testObject.override('c').contents, { 'a': { 'b': 1, 'd': 1 }, 'f': 1, 'g': 1 });
});
@@ -127,7 +130,7 @@ suite('ConfigurationModel', () => {
test('get overriding configuration when one of the key in base is not of object type', () => {
let testObject = new ConfigurationModel(
{ 'a': { 'b': 1 }, 'f': 1 }, [],
[{ identifiers: ['c'], contents: { 'a': { 'd': 1 }, 'f': { 'g': 1 } } }]);
[{ identifiers: ['c'], contents: { 'a': { 'd': 1 }, 'f': { 'g': 1 } }, keys: ['a', 'f'] }]);
assert.deepEqual(testObject.override('c').contents, { 'a': { 'b': 1, 'd': 1 }, 'f': { 'g': 1 } });
});
@@ -135,7 +138,7 @@ suite('ConfigurationModel', () => {
test('get overriding configuration when one of the key in overriding contents is not of object type', () => {
let testObject = new ConfigurationModel(
{ 'a': { 'b': 1 }, 'f': { 'g': 1 } }, [],
[{ identifiers: ['c'], contents: { 'a': { 'd': 1 }, 'f': 1 } }]);
[{ identifiers: ['c'], contents: { 'a': { 'd': 1 }, 'f': 1 }, keys: ['a', 'f'] }]);
assert.deepEqual(testObject.override('c').contents, { 'a': { 'b': 1, 'd': 1 }, 'f': 1 });
});
@@ -143,7 +146,7 @@ suite('ConfigurationModel', () => {
test('get overriding configuration if the value of overriding identifier is not object', () => {
let testObject = new ConfigurationModel(
{ 'a': { 'b': 1 }, 'f': { 'g': 1 } }, [],
[{ identifiers: ['c'], contents: 'abc' }]);
[{ identifiers: ['c'], contents: 'abc', keys: [] }]);
assert.deepEqual(testObject.override('c').contents, { 'a': { 'b': 1 }, 'f': { 'g': 1 } });
});
@@ -151,7 +154,7 @@ suite('ConfigurationModel', () => {
test('get overriding configuration if the value of overriding identifier is an empty object', () => {
let testObject = new ConfigurationModel(
{ 'a': { 'b': 1 }, 'f': { 'g': 1 } }, [],
[{ identifiers: ['c'], contents: {} }]);
[{ identifiers: ['c'], contents: {}, keys: [] }]);
assert.deepEqual(testObject.override('c').contents, { 'a': { 'b': 1 }, 'f': { 'g': 1 } });
});
@@ -176,34 +179,34 @@ suite('ConfigurationModel', () => {
});
test('simple merge overrides', () => {
let base = new ConfigurationModel({ 'a': { 'b': 1 } }, ['a.b'], [{ identifiers: ['c'], contents: { 'a': 2 } }]);
let add = new ConfigurationModel({ 'a': { 'b': 2 } }, ['a.b'], [{ identifiers: ['c'], contents: { 'b': 2 } }]);
let base = new ConfigurationModel({ 'a': { 'b': 1 } }, ['a.b'], [{ identifiers: ['c'], contents: { 'a': 2 }, keys: ['a'] }]);
let add = new ConfigurationModel({ 'a': { 'b': 2 } }, ['a.b'], [{ identifiers: ['c'], contents: { 'b': 2 }, keys: ['b'] }]);
let result = base.merge(add);
assert.deepEqual(result.contents, { 'a': { 'b': 2 } });
assert.deepEqual(result.overrides, [{ identifiers: ['c'], contents: { 'a': 2, 'b': 2 } }]);
assert.deepEqual(result.overrides, [{ identifiers: ['c'], contents: { 'a': 2, 'b': 2 }, keys: ['a'] }]);
assert.deepEqual(result.override('c').contents, { 'a': 2, 'b': 2 });
assert.deepEqual(result.keys, ['a.b']);
});
test('recursive merge overrides', () => {
let base = new ConfigurationModel({ 'a': { 'b': 1 }, 'f': 1 }, ['a.b', 'f'], [{ identifiers: ['c'], contents: { 'a': { 'd': 1 } } }]);
let add = new ConfigurationModel({ 'a': { 'b': 2 } }, ['a.b'], [{ identifiers: ['c'], contents: { 'a': { 'e': 2 } } }]);
let base = new ConfigurationModel({ 'a': { 'b': 1 }, 'f': 1 }, ['a.b', 'f'], [{ identifiers: ['c'], contents: { 'a': { 'd': 1 } }, keys: ['a'] }]);
let add = new ConfigurationModel({ 'a': { 'b': 2 } }, ['a.b'], [{ identifiers: ['c'], contents: { 'a': { 'e': 2 } }, keys: ['a'] }]);
let result = base.merge(add);
assert.deepEqual(result.contents, { 'a': { 'b': 2 }, 'f': 1 });
assert.deepEqual(result.overrides, [{ identifiers: ['c'], contents: { 'a': { 'd': 1, 'e': 2 } } }]);
assert.deepEqual(result.overrides, [{ identifiers: ['c'], contents: { 'a': { 'd': 1, 'e': 2 } }, keys: ['a'] }]);
assert.deepEqual(result.override('c').contents, { 'a': { 'b': 2, 'd': 1, 'e': 2 }, 'f': 1 });
assert.deepEqual(result.keys, ['a.b', 'f']);
});
test('merge overrides when frozen', () => {
let model1 = new ConfigurationModel({ 'a': { 'b': 1 }, 'f': 1 }, ['a.b', 'f'], [{ identifiers: ['c'], contents: { 'a': { 'd': 1 } } }]).freeze();
let model2 = new ConfigurationModel({ 'a': { 'b': 2 } }, ['a.b'], [{ identifiers: ['c'], contents: { 'a': { 'e': 2 } } }]).freeze();
let model1 = new ConfigurationModel({ 'a': { 'b': 1 }, 'f': 1 }, ['a.b', 'f'], [{ identifiers: ['c'], contents: { 'a': { 'd': 1 } }, keys: ['a'] }]).freeze();
let model2 = new ConfigurationModel({ 'a': { 'b': 2 } }, ['a.b'], [{ identifiers: ['c'], contents: { 'a': { 'e': 2 } }, keys: ['a'] }]).freeze();
let result = new ConfigurationModel().merge(model1, model2);
assert.deepEqual(result.contents, { 'a': { 'b': 2 }, 'f': 1 });
assert.deepEqual(result.overrides, [{ identifiers: ['c'], contents: { 'a': { 'd': 1, 'e': 2 } } }]);
assert.deepEqual(result.overrides, [{ identifiers: ['c'], contents: { 'a': { 'd': 1, 'e': 2 } }, keys: ['a'] }]);
assert.deepEqual(result.override('c').contents, { 'a': { 'b': 2, 'd': 1, 'e': 2 }, 'f': 1 });
assert.deepEqual(result.keys, ['a.b', 'f']);
});
@@ -223,7 +226,7 @@ suite('ConfigurationModel', () => {
});
test('Test override gives all content merged with overrides', () => {
const testObject = new ConfigurationModel({ 'a': 1, 'c': 1 }, [], [{ identifiers: ['b'], contents: { 'a': 2 } }]);
const testObject = new ConfigurationModel({ 'a': 1, 'c': 1 }, [], [{ identifiers: ['b'], contents: { 'a': 2 }, keys: ['a'] }]);
assert.deepEqual(testObject.override('b').contents, { 'a': 2, 'c': 1 });
});
@@ -360,114 +363,6 @@ suite('CustomConfigurationModel', () => {
});
});
suite('ConfigurationChangeEvent', () => {
test('changeEvent affecting keys for all resources', () => {
let testObject = new ConfigurationChangeEvent();
testObject.change(['window.zoomLevel', 'workbench.editor.enablePreview', 'files', '[markdown]']);
assert.deepEqual(testObject.affectedKeys, ['window.zoomLevel', 'workbench.editor.enablePreview', 'files', '[markdown]']);
assert.ok(testObject.affectsConfiguration('window.zoomLevel'));
assert.ok(testObject.affectsConfiguration('window'));
assert.ok(testObject.affectsConfiguration('workbench.editor.enablePreview'));
assert.ok(testObject.affectsConfiguration('workbench.editor'));
assert.ok(testObject.affectsConfiguration('workbench'));
assert.ok(testObject.affectsConfiguration('files'));
assert.ok(!testObject.affectsConfiguration('files.exclude'));
assert.ok(testObject.affectsConfiguration('[markdown]'));
});
test('changeEvent affecting a root key and its children', () => {
let testObject = new ConfigurationChangeEvent();
testObject.change(['launch', 'launch.version', 'tasks']);
assert.deepEqual(testObject.affectedKeys, ['launch.version', 'tasks']);
assert.ok(testObject.affectsConfiguration('launch'));
assert.ok(testObject.affectsConfiguration('launch.version'));
assert.ok(testObject.affectsConfiguration('tasks'));
});
test('changeEvent affecting keys for resources', () => {
let testObject = new ConfigurationChangeEvent();
testObject.change(['window.title']);
testObject.change(['window.zoomLevel'], URI.file('file1'));
testObject.change(['workbench.editor.enablePreview'], URI.file('file2'));
testObject.change(['window.restoreFullscreen'], URI.file('file1'));
testObject.change(['window.restoreWindows'], URI.file('file2'));
assert.deepEqual(testObject.affectedKeys, ['window.title', 'window.zoomLevel', 'window.restoreFullscreen', 'workbench.editor.enablePreview', 'window.restoreWindows']);
assert.ok(testObject.affectsConfiguration('window.zoomLevel'));
assert.ok(testObject.affectsConfiguration('window.zoomLevel', URI.file('file1')));
assert.ok(!testObject.affectsConfiguration('window.zoomLevel', URI.file('file2')));
assert.ok(testObject.affectsConfiguration('window.restoreFullscreen'));
assert.ok(testObject.affectsConfiguration('window.restoreFullscreen', URI.file('file1')));
assert.ok(!testObject.affectsConfiguration('window.restoreFullscreen', URI.file('file2')));
assert.ok(testObject.affectsConfiguration('window.restoreWindows'));
assert.ok(testObject.affectsConfiguration('window.restoreWindows', URI.file('file2')));
assert.ok(!testObject.affectsConfiguration('window.restoreWindows', URI.file('file1')));
assert.ok(testObject.affectsConfiguration('window.title'));
assert.ok(testObject.affectsConfiguration('window.title', URI.file('file1')));
assert.ok(testObject.affectsConfiguration('window.title', URI.file('file2')));
assert.ok(testObject.affectsConfiguration('window'));
assert.ok(testObject.affectsConfiguration('window', URI.file('file1')));
assert.ok(testObject.affectsConfiguration('window', URI.file('file2')));
assert.ok(testObject.affectsConfiguration('workbench.editor.enablePreview'));
assert.ok(testObject.affectsConfiguration('workbench.editor.enablePreview', URI.file('file2')));
assert.ok(!testObject.affectsConfiguration('workbench.editor.enablePreview', URI.file('file1')));
assert.ok(testObject.affectsConfiguration('workbench.editor'));
assert.ok(testObject.affectsConfiguration('workbench.editor', URI.file('file2')));
assert.ok(!testObject.affectsConfiguration('workbench.editor', URI.file('file1')));
assert.ok(testObject.affectsConfiguration('workbench'));
assert.ok(testObject.affectsConfiguration('workbench', URI.file('file2')));
assert.ok(!testObject.affectsConfiguration('workbench', URI.file('file1')));
assert.ok(!testObject.affectsConfiguration('files'));
assert.ok(!testObject.affectsConfiguration('files', URI.file('file1')));
assert.ok(!testObject.affectsConfiguration('files', URI.file('file2')));
});
test('merging change events', () => {
let event1 = new ConfigurationChangeEvent().change(['window.zoomLevel', 'files']);
let event2 = new ConfigurationChangeEvent().change(['window.title'], URI.file('file1')).change(['[markdown]']);
let actual = event1.change(event2);
assert.deepEqual(actual.affectedKeys, ['window.zoomLevel', 'files', '[markdown]', 'window.title']);
assert.ok(actual.affectsConfiguration('window.zoomLevel'));
assert.ok(actual.affectsConfiguration('window.zoomLevel', URI.file('file1')));
assert.ok(actual.affectsConfiguration('window.zoomLevel', URI.file('file2')));
assert.ok(actual.affectsConfiguration('window'));
assert.ok(actual.affectsConfiguration('window', URI.file('file1')));
assert.ok(actual.affectsConfiguration('window', URI.file('file2')));
assert.ok(actual.affectsConfiguration('files'));
assert.ok(actual.affectsConfiguration('files', URI.file('file1')));
assert.ok(actual.affectsConfiguration('files', URI.file('file2')));
assert.ok(actual.affectsConfiguration('window.title'));
assert.ok(actual.affectsConfiguration('window.title', URI.file('file1')));
assert.ok(!actual.affectsConfiguration('window.title', URI.file('file2')));
assert.ok(actual.affectsConfiguration('[markdown]'));
assert.ok(actual.affectsConfiguration('[markdown]', URI.file('file1')));
assert.ok(actual.affectsConfiguration('[markdown]', URI.file('file2')));
});
});
suite('Configuration', () => {
test('Test update value', () => {
@@ -491,5 +386,552 @@ suite('Configuration', () => {
assert.equal(testObject.getValue('a', {}, undefined), 2);
});
test('Test compare and update default configuration', () => {
const testObject = new Configuration(new ConfigurationModel(), new ConfigurationModel());
testObject.updateDefaultConfiguration(toConfigurationModel({
'editor.lineNumbers': 'on',
}));
});
const actual = testObject.compareAndUpdateDefaultConfiguration(toConfigurationModel({
'editor.lineNumbers': 'off',
'[markdown]': {
'editor.wordWrap': 'off'
}
}), ['editor.lineNumbers', '[markdown]']);
assert.deepEqual(actual, { keys: ['editor.lineNumbers', '[markdown]'], overrides: [['markdown', ['editor.wordWrap']]] });
});
test('Test compare and update user configuration', () => {
const testObject = new Configuration(new ConfigurationModel(), new ConfigurationModel());
testObject.updateLocalUserConfiguration(toConfigurationModel({
'editor.lineNumbers': 'off',
'editor.fontSize': 12,
'[typescript]': {
'editor.wordWrap': 'off'
}
}));
const actual = testObject.compareAndUpdateLocalUserConfiguration(toConfigurationModel({
'editor.lineNumbers': 'on',
'window.zoomLevel': 1,
'[typescript]': {
'editor.wordWrap': 'on',
'editor.insertSpaces': false
}
}));
assert.deepEqual(actual, { keys: ['window.zoomLevel', 'editor.lineNumbers', '[typescript]', 'editor.fontSize'], overrides: [['typescript', ['editor.insertSpaces', 'editor.wordWrap']]] });
});
test('Test compare and update workspace configuration', () => {
const testObject = new Configuration(new ConfigurationModel(), new ConfigurationModel());
testObject.updateWorkspaceConfiguration(toConfigurationModel({
'editor.lineNumbers': 'off',
'editor.fontSize': 12,
'[typescript]': {
'editor.wordWrap': 'off'
}
}));
const actual = testObject.compareAndUpdateWorkspaceConfiguration(toConfigurationModel({
'editor.lineNumbers': 'on',
'window.zoomLevel': 1,
'[typescript]': {
'editor.wordWrap': 'on',
'editor.insertSpaces': false
}
}));
assert.deepEqual(actual, { keys: ['window.zoomLevel', 'editor.lineNumbers', '[typescript]', 'editor.fontSize'], overrides: [['typescript', ['editor.insertSpaces', 'editor.wordWrap']]] });
});
test('Test compare and update workspace folder configuration', () => {
const testObject = new Configuration(new ConfigurationModel(), new ConfigurationModel());
testObject.updateFolderConfiguration(URI.file('file1'), toConfigurationModel({
'editor.lineNumbers': 'off',
'editor.fontSize': 12,
'[typescript]': {
'editor.wordWrap': 'off'
}
}));
const actual = testObject.compareAndUpdateFolderConfiguration(URI.file('file1'), toConfigurationModel({
'editor.lineNumbers': 'on',
'window.zoomLevel': 1,
'[typescript]': {
'editor.wordWrap': 'on',
'editor.insertSpaces': false
}
}));
assert.deepEqual(actual, { keys: ['window.zoomLevel', 'editor.lineNumbers', '[typescript]', 'editor.fontSize'], overrides: [['typescript', ['editor.insertSpaces', 'editor.wordWrap']]] });
});
test('Test compare and deletre workspace folder configuration', () => {
const testObject = new Configuration(new ConfigurationModel(), new ConfigurationModel());
testObject.updateFolderConfiguration(URI.file('file1'), toConfigurationModel({
'editor.lineNumbers': 'off',
'editor.fontSize': 12,
'[typescript]': {
'editor.wordWrap': 'off'
}
}));
const actual = testObject.compareAndDeleteFolderConfiguration(URI.file('file1'));
assert.deepEqual(actual, { keys: ['editor.lineNumbers', 'editor.fontSize', '[typescript]'], overrides: [['typescript', ['editor.wordWrap']]] });
});
});
suite('ConfigurationChangeEvent', () => {
test('changeEvent affecting keys with new configuration', () => {
const configuration = new Configuration(new ConfigurationModel(), new ConfigurationModel());
const change = configuration.compareAndUpdateLocalUserConfiguration(toConfigurationModel({
'window.zoomLevel': 1,
'workbench.editor.enablePreview': false,
'files.autoSave': 'off',
}));
let testObject = new ConfigurationChangeEvent(change, undefined, configuration);
assert.deepEqual(testObject.affectedKeys, ['window.zoomLevel', 'workbench.editor.enablePreview', 'files.autoSave']);
assert.ok(testObject.affectsConfiguration('window.zoomLevel'));
assert.ok(testObject.affectsConfiguration('window'));
assert.ok(testObject.affectsConfiguration('workbench.editor.enablePreview'));
assert.ok(testObject.affectsConfiguration('workbench.editor'));
assert.ok(testObject.affectsConfiguration('workbench'));
assert.ok(testObject.affectsConfiguration('files'));
assert.ok(testObject.affectsConfiguration('files.autoSave'));
assert.ok(!testObject.affectsConfiguration('files.exclude'));
assert.ok(!testObject.affectsConfiguration('[markdown]'));
assert.ok(!testObject.affectsConfiguration('editor'));
});
test('changeEvent affecting keys when configuration changed', () => {
const configuration = new Configuration(new ConfigurationModel(), new ConfigurationModel());
configuration.updateLocalUserConfiguration(toConfigurationModel({
'window.zoomLevel': 2,
'workbench.editor.enablePreview': true,
'files.autoSave': 'off',
}));
const data = configuration.toData();
const change = configuration.compareAndUpdateLocalUserConfiguration(toConfigurationModel({
'window.zoomLevel': 1,
'workbench.editor.enablePreview': false,
'files.autoSave': 'off',
}));
let testObject = new ConfigurationChangeEvent(change, { data }, configuration);
assert.deepEqual(testObject.affectedKeys, ['window.zoomLevel', 'workbench.editor.enablePreview']);
assert.ok(testObject.affectsConfiguration('window.zoomLevel'));
assert.ok(testObject.affectsConfiguration('window'));
assert.ok(testObject.affectsConfiguration('workbench.editor.enablePreview'));
assert.ok(testObject.affectsConfiguration('workbench.editor'));
assert.ok(testObject.affectsConfiguration('workbench'));
assert.ok(!testObject.affectsConfiguration('files'));
assert.ok(!testObject.affectsConfiguration('[markdown]'));
assert.ok(!testObject.affectsConfiguration('editor'));
});
test('changeEvent affecting overrides with new configuration', () => {
const configuration = new Configuration(new ConfigurationModel(), new ConfigurationModel());
const change = configuration.compareAndUpdateLocalUserConfiguration(toConfigurationModel({
'files.autoSave': 'off',
'[markdown]': {
'editor.wordWrap': 'off'
}
}));
let testObject = new ConfigurationChangeEvent(change, undefined, configuration);
assert.deepEqual(testObject.affectedKeys, ['files.autoSave', '[markdown]', 'editor.wordWrap']);
assert.ok(testObject.affectsConfiguration('files'));
assert.ok(testObject.affectsConfiguration('files.autoSave'));
assert.ok(!testObject.affectsConfiguration('files.exclude'));
assert.ok(testObject.affectsConfiguration('[markdown]'));
assert.ok(!testObject.affectsConfiguration('[markdown].editor'));
assert.ok(!testObject.affectsConfiguration('[markdown].workbench'));
assert.ok(testObject.affectsConfiguration('editor'));
assert.ok(testObject.affectsConfiguration('editor.wordWrap'));
assert.ok(testObject.affectsConfiguration('editor', { overrideIdentifier: 'markdown' }));
assert.ok(testObject.affectsConfiguration('editor.wordWrap', { overrideIdentifier: 'markdown' }));
assert.ok(!testObject.affectsConfiguration('editor', { overrideIdentifier: 'json' }));
assert.ok(!testObject.affectsConfiguration('editor.fontSize', { overrideIdentifier: 'markdown' }));
assert.ok(!testObject.affectsConfiguration('editor.fontSize'));
assert.ok(!testObject.affectsConfiguration('window'));
});
test('changeEvent affecting overrides when configuration changed', () => {
const configuration = new Configuration(new ConfigurationModel(), new ConfigurationModel());
configuration.updateLocalUserConfiguration(toConfigurationModel({
'workbench.editor.enablePreview': true,
'[markdown]': {
'editor.fontSize': 12,
'editor.wordWrap': 'off'
},
'files.autoSave': 'off',
}));
const data = configuration.toData();
const change = configuration.compareAndUpdateLocalUserConfiguration(toConfigurationModel({
'files.autoSave': 'off',
'[markdown]': {
'editor.fontSize': 13,
'editor.wordWrap': 'off'
},
'window.zoomLevel': 1,
}));
let testObject = new ConfigurationChangeEvent(change, { data }, configuration);
assert.deepEqual(testObject.affectedKeys, ['window.zoomLevel', '[markdown]', 'workbench.editor.enablePreview', 'editor.fontSize']);
assert.ok(!testObject.affectsConfiguration('files'));
assert.ok(testObject.affectsConfiguration('[markdown]'));
assert.ok(!testObject.affectsConfiguration('[markdown].editor'));
assert.ok(!testObject.affectsConfiguration('[markdown].editor.fontSize'));
assert.ok(!testObject.affectsConfiguration('[markdown].editor.wordWrap'));
assert.ok(!testObject.affectsConfiguration('[markdown].workbench'));
assert.ok(testObject.affectsConfiguration('editor'));
assert.ok(testObject.affectsConfiguration('editor', { overrideIdentifier: 'markdown' }));
assert.ok(testObject.affectsConfiguration('editor.fontSize', { overrideIdentifier: 'markdown' }));
assert.ok(!testObject.affectsConfiguration('editor.wordWrap'));
assert.ok(!testObject.affectsConfiguration('editor.wordWrap', { overrideIdentifier: 'markdown' }));
assert.ok(!testObject.affectsConfiguration('editor', { overrideIdentifier: 'json' }));
assert.ok(!testObject.affectsConfiguration('editor.fontSize', { overrideIdentifier: 'json' }));
assert.ok(testObject.affectsConfiguration('window'));
assert.ok(testObject.affectsConfiguration('window.zoomLevel'));
assert.ok(testObject.affectsConfiguration('window', { overrideIdentifier: 'markdown' }));
assert.ok(testObject.affectsConfiguration('window.zoomLevel', { overrideIdentifier: 'markdown' }));
assert.ok(testObject.affectsConfiguration('workbench'));
assert.ok(testObject.affectsConfiguration('workbench.editor'));
assert.ok(testObject.affectsConfiguration('workbench.editor.enablePreview'));
assert.ok(testObject.affectsConfiguration('workbench', { overrideIdentifier: 'markdown' }));
assert.ok(testObject.affectsConfiguration('workbench.editor', { overrideIdentifier: 'markdown' }));
});
test('changeEvent affecting workspace folders', () => {
const configuration = new Configuration(new ConfigurationModel(), new ConfigurationModel());
configuration.updateWorkspaceConfiguration(toConfigurationModel({ 'window.title': 'custom' }));
configuration.updateFolderConfiguration(URI.file('folder1'), toConfigurationModel({ 'window.zoomLevel': 2, 'window.restoreFullscreen': true }));
configuration.updateFolderConfiguration(URI.file('folder2'), toConfigurationModel({ 'workbench.editor.enablePreview': true, 'window.restoreWindows': true }));
const data = configuration.toData();
const workspace = new Workspace('a', [new WorkspaceFolder({ index: 0, name: 'a', uri: URI.file('folder1') }), new WorkspaceFolder({ index: 1, name: 'b', uri: URI.file('folder2') }), new WorkspaceFolder({ index: 2, name: 'c', uri: URI.file('folder3') })]);
const change = mergeChanges(
configuration.compareAndUpdateWorkspaceConfiguration(toConfigurationModel({ 'window.title': 'native' })),
configuration.compareAndUpdateFolderConfiguration(URI.file('folder1'), toConfigurationModel({ 'window.zoomLevel': 1, 'window.restoreFullscreen': false })),
configuration.compareAndUpdateFolderConfiguration(URI.file('folder2'), toConfigurationModel({ 'workbench.editor.enablePreview': false, 'window.restoreWindows': false }))
);
let testObject = new ConfigurationChangeEvent(change, { data, workspace }, configuration, workspace);
assert.deepEqual(testObject.affectedKeys, ['window.title', 'window.zoomLevel', 'window.restoreFullscreen', 'workbench.editor.enablePreview', 'window.restoreWindows']);
assert.ok(testObject.affectsConfiguration('window.zoomLevel'));
assert.ok(testObject.affectsConfiguration('window.zoomLevel', { resource: URI.file('folder1') }));
assert.ok(testObject.affectsConfiguration('window.zoomLevel', { resource: URI.file(join('folder1', 'file1')) }));
assert.ok(!testObject.affectsConfiguration('window.zoomLevel', { resource: URI.file('file1') }));
assert.ok(!testObject.affectsConfiguration('window.zoomLevel', { resource: URI.file('file2') }));
assert.ok(!testObject.affectsConfiguration('window.zoomLevel', { resource: URI.file(join('folder2', 'file2')) }));
assert.ok(!testObject.affectsConfiguration('window.zoomLevel', { resource: URI.file(join('folder3', 'file3')) }));
assert.ok(testObject.affectsConfiguration('window.restoreFullscreen'));
assert.ok(testObject.affectsConfiguration('window.restoreFullscreen', { resource: URI.file(join('folder1', 'file1')) }));
assert.ok(testObject.affectsConfiguration('window.restoreFullscreen', { resource: URI.file('folder1') }));
assert.ok(!testObject.affectsConfiguration('window.restoreFullscreen', { resource: URI.file('file1') }));
assert.ok(!testObject.affectsConfiguration('window.restoreFullscreen', { resource: URI.file('file2') }));
assert.ok(!testObject.affectsConfiguration('window.restoreFullscreen', { resource: URI.file(join('folder2', 'file2')) }));
assert.ok(!testObject.affectsConfiguration('window.restoreFullscreen', { resource: URI.file(join('folder3', 'file3')) }));
assert.ok(testObject.affectsConfiguration('window.restoreWindows'));
assert.ok(testObject.affectsConfiguration('window.restoreWindows', { resource: URI.file('folder2') }));
assert.ok(testObject.affectsConfiguration('window.restoreWindows', { resource: URI.file(join('folder2', 'file2')) }));
assert.ok(!testObject.affectsConfiguration('window.restoreWindows', { resource: URI.file('file2') }));
assert.ok(!testObject.affectsConfiguration('window.restoreWindows', { resource: URI.file(join('folder1', 'file1')) }));
assert.ok(!testObject.affectsConfiguration('window.restoreWindows', { resource: URI.file(join('folder3', 'file3')) }));
assert.ok(testObject.affectsConfiguration('window.title'));
assert.ok(testObject.affectsConfiguration('window.title', { resource: URI.file('folder1') }));
assert.ok(testObject.affectsConfiguration('window.title', { resource: URI.file(join('folder1', 'file1')) }));
assert.ok(testObject.affectsConfiguration('window.title', { resource: URI.file('folder2') }));
assert.ok(testObject.affectsConfiguration('window.title', { resource: URI.file(join('folder2', 'file2')) }));
assert.ok(testObject.affectsConfiguration('window.title', { resource: URI.file('folder3') }));
assert.ok(testObject.affectsConfiguration('window.title', { resource: URI.file(join('folder3', 'file3')) }));
assert.ok(testObject.affectsConfiguration('window.title', { resource: URI.file('file1') }));
assert.ok(testObject.affectsConfiguration('window.title', { resource: URI.file('file2') }));
assert.ok(testObject.affectsConfiguration('window.title', { resource: URI.file('file3') }));
assert.ok(testObject.affectsConfiguration('window'));
assert.ok(testObject.affectsConfiguration('window', { resource: URI.file('folder1') }));
assert.ok(testObject.affectsConfiguration('window', { resource: URI.file(join('folder1', 'file1')) }));
assert.ok(testObject.affectsConfiguration('window', { resource: URI.file('folder2') }));
assert.ok(testObject.affectsConfiguration('window', { resource: URI.file(join('folder2', 'file2')) }));
assert.ok(testObject.affectsConfiguration('window', { resource: URI.file('folder3') }));
assert.ok(testObject.affectsConfiguration('window', { resource: URI.file(join('folder3', 'file3')) }));
assert.ok(testObject.affectsConfiguration('window', { resource: URI.file('file1') }));
assert.ok(testObject.affectsConfiguration('window', { resource: URI.file('file2') }));
assert.ok(testObject.affectsConfiguration('window', { resource: URI.file('file3') }));
assert.ok(testObject.affectsConfiguration('workbench.editor.enablePreview'));
assert.ok(testObject.affectsConfiguration('workbench.editor.enablePreview', { resource: URI.file('folder2') }));
assert.ok(testObject.affectsConfiguration('workbench.editor.enablePreview', { resource: URI.file(join('folder2', 'file2')) }));
assert.ok(!testObject.affectsConfiguration('workbench.editor.enablePreview', { resource: URI.file('folder1') }));
assert.ok(!testObject.affectsConfiguration('workbench.editor.enablePreview', { resource: URI.file(join('folder1', 'file1')) }));
assert.ok(!testObject.affectsConfiguration('workbench.editor.enablePreview', { resource: URI.file('folder3') }));
assert.ok(testObject.affectsConfiguration('workbench.editor'));
assert.ok(testObject.affectsConfiguration('workbench.editor', { resource: URI.file('folder2') }));
assert.ok(testObject.affectsConfiguration('workbench.editor', { resource: URI.file(join('folder2', 'file2')) }));
assert.ok(!testObject.affectsConfiguration('workbench.editor', { resource: URI.file('folder1') }));
assert.ok(!testObject.affectsConfiguration('workbench.editor', { resource: URI.file(join('folder1', 'file1')) }));
assert.ok(!testObject.affectsConfiguration('workbench.editor', { resource: URI.file('folder3') }));
assert.ok(testObject.affectsConfiguration('workbench'));
assert.ok(testObject.affectsConfiguration('workbench', { resource: URI.file('folder2') }));
assert.ok(testObject.affectsConfiguration('workbench', { resource: URI.file(join('folder2', 'file2')) }));
assert.ok(!testObject.affectsConfiguration('workbench', { resource: URI.file('folder1') }));
assert.ok(!testObject.affectsConfiguration('workbench', { resource: URI.file('folder3') }));
assert.ok(!testObject.affectsConfiguration('files'));
assert.ok(!testObject.affectsConfiguration('files', { resource: URI.file('folder1') }));
assert.ok(!testObject.affectsConfiguration('files', { resource: URI.file(join('folder1', 'file1')) }));
assert.ok(!testObject.affectsConfiguration('files', { resource: URI.file('folder2') }));
assert.ok(!testObject.affectsConfiguration('files', { resource: URI.file(join('folder2', 'file2')) }));
assert.ok(!testObject.affectsConfiguration('files', { resource: URI.file('folder3') }));
assert.ok(!testObject.affectsConfiguration('files', { resource: URI.file(join('folder3', 'file3')) }));
});
test('changeEvent - all', () => {
const configuration = new Configuration(new ConfigurationModel(), new ConfigurationModel());
configuration.updateFolderConfiguration(URI.file('file1'), toConfigurationModel({ 'window.zoomLevel': 2, 'window.restoreFullscreen': true }));
const data = configuration.toData();
const change = mergeChanges(
configuration.compareAndUpdateDefaultConfiguration(toConfigurationModel({
'editor.lineNumbers': 'off',
'[markdown]': {
'editor.wordWrap': 'off'
}
}), ['editor.lineNumbers', '[markdown]']),
configuration.compareAndUpdateLocalUserConfiguration(toConfigurationModel({
'[json]': {
'editor.lineNumbers': 'relative'
}
})),
configuration.compareAndUpdateWorkspaceConfiguration(toConfigurationModel({ 'window.title': 'custom' })),
configuration.compareAndDeleteFolderConfiguration(URI.file('file1')),
configuration.compareAndUpdateFolderConfiguration(URI.file('file2'), toConfigurationModel({ 'workbench.editor.enablePreview': true, 'window.restoreWindows': true })));
const workspace = new Workspace('a', [new WorkspaceFolder({ index: 0, name: 'a', uri: URI.file('file1') }), new WorkspaceFolder({ index: 1, name: 'b', uri: URI.file('file2') }), new WorkspaceFolder({ index: 2, name: 'c', uri: URI.file('folder3') })]);
const testObject = new ConfigurationChangeEvent(change, { data, workspace }, configuration, workspace);
assert.deepEqual(testObject.affectedKeys, ['editor.lineNumbers', '[markdown]', '[json]', 'window.title', 'window.zoomLevel', 'window.restoreFullscreen', 'workbench.editor.enablePreview', 'window.restoreWindows', 'editor.wordWrap']);
assert.ok(testObject.affectsConfiguration('window.title'));
assert.ok(testObject.affectsConfiguration('window.title', { resource: URI.file('file1') }));
assert.ok(testObject.affectsConfiguration('window.title', { resource: URI.file('file2') }));
assert.ok(testObject.affectsConfiguration('window'));
assert.ok(testObject.affectsConfiguration('window', { resource: URI.file('file1') }));
assert.ok(testObject.affectsConfiguration('window', { resource: URI.file('file2') }));
assert.ok(testObject.affectsConfiguration('window.zoomLevel'));
assert.ok(testObject.affectsConfiguration('window.zoomLevel', { resource: URI.file('file1') }));
assert.ok(!testObject.affectsConfiguration('window.zoomLevel', { resource: URI.file('file2') }));
assert.ok(testObject.affectsConfiguration('window.restoreFullscreen'));
assert.ok(testObject.affectsConfiguration('window.restoreFullscreen', { resource: URI.file('file1') }));
assert.ok(!testObject.affectsConfiguration('window.restoreFullscreen', { resource: URI.file('file2') }));
assert.ok(testObject.affectsConfiguration('window.restoreWindows'));
assert.ok(testObject.affectsConfiguration('window.restoreWindows', { resource: URI.file('file2') }));
assert.ok(!testObject.affectsConfiguration('window.restoreWindows', { resource: URI.file('file1') }));
assert.ok(testObject.affectsConfiguration('workbench.editor.enablePreview'));
assert.ok(testObject.affectsConfiguration('workbench.editor.enablePreview', { resource: URI.file('file2') }));
assert.ok(!testObject.affectsConfiguration('workbench.editor.enablePreview', { resource: URI.file('file1') }));
assert.ok(testObject.affectsConfiguration('workbench.editor'));
assert.ok(testObject.affectsConfiguration('workbench.editor', { resource: URI.file('file2') }));
assert.ok(!testObject.affectsConfiguration('workbench.editor', { resource: URI.file('file1') }));
assert.ok(testObject.affectsConfiguration('workbench'));
assert.ok(testObject.affectsConfiguration('workbench', { resource: URI.file('file2') }));
assert.ok(!testObject.affectsConfiguration('workbench', { resource: URI.file('file1') }));
assert.ok(!testObject.affectsConfiguration('files'));
assert.ok(!testObject.affectsConfiguration('files', { resource: URI.file('file1') }));
assert.ok(!testObject.affectsConfiguration('files', { resource: URI.file('file2') }));
assert.ok(testObject.affectsConfiguration('editor'));
assert.ok(testObject.affectsConfiguration('editor', { resource: URI.file('file1') }));
assert.ok(testObject.affectsConfiguration('editor', { resource: URI.file('file2') }));
assert.ok(testObject.affectsConfiguration('editor', { resource: URI.file('file1'), overrideIdentifier: 'json' }));
assert.ok(testObject.affectsConfiguration('editor', { resource: URI.file('file1'), overrideIdentifier: 'markdown' }));
assert.ok(testObject.affectsConfiguration('editor', { resource: URI.file('file1'), overrideIdentifier: 'typescript' }));
assert.ok(testObject.affectsConfiguration('editor', { resource: URI.file('file2'), overrideIdentifier: 'json' }));
assert.ok(testObject.affectsConfiguration('editor', { resource: URI.file('file2'), overrideIdentifier: 'markdown' }));
assert.ok(testObject.affectsConfiguration('editor', { resource: URI.file('file2'), overrideIdentifier: 'typescript' }));
assert.ok(testObject.affectsConfiguration('editor.lineNumbers'));
assert.ok(testObject.affectsConfiguration('editor.lineNumbers', { resource: URI.file('file1') }));
assert.ok(testObject.affectsConfiguration('editor.lineNumbers', { resource: URI.file('file2') }));
assert.ok(testObject.affectsConfiguration('editor.lineNumbers', { resource: URI.file('file1'), overrideIdentifier: 'json' }));
assert.ok(testObject.affectsConfiguration('editor.lineNumbers', { resource: URI.file('file1'), overrideIdentifier: 'markdown' }));
assert.ok(testObject.affectsConfiguration('editor.lineNumbers', { resource: URI.file('file1'), overrideIdentifier: 'typescript' }));
assert.ok(testObject.affectsConfiguration('editor.lineNumbers', { resource: URI.file('file2'), overrideIdentifier: 'json' }));
assert.ok(testObject.affectsConfiguration('editor.lineNumbers', { resource: URI.file('file2'), overrideIdentifier: 'markdown' }));
assert.ok(testObject.affectsConfiguration('editor.lineNumbers', { resource: URI.file('file2'), overrideIdentifier: 'typescript' }));
assert.ok(testObject.affectsConfiguration('editor.wordWrap'));
assert.ok(!testObject.affectsConfiguration('editor.wordWrap', { resource: URI.file('file1') }));
assert.ok(!testObject.affectsConfiguration('editor.wordWrap', { resource: URI.file('file2') }));
assert.ok(!testObject.affectsConfiguration('editor.wordWrap', { resource: URI.file('file1'), overrideIdentifier: 'json' }));
assert.ok(testObject.affectsConfiguration('editor.wordWrap', { resource: URI.file('file1'), overrideIdentifier: 'markdown' }));
assert.ok(!testObject.affectsConfiguration('editor.wordWrap', { resource: URI.file('file1'), overrideIdentifier: 'typescript' }));
assert.ok(!testObject.affectsConfiguration('editor.wordWrap', { resource: URI.file('file2'), overrideIdentifier: 'json' }));
assert.ok(testObject.affectsConfiguration('editor.wordWrap', { resource: URI.file('file2'), overrideIdentifier: 'markdown' }));
assert.ok(!testObject.affectsConfiguration('editor.wordWrap', { resource: URI.file('file2'), overrideIdentifier: 'typescript' }));
assert.ok(!testObject.affectsConfiguration('editor.fontSize'));
assert.ok(!testObject.affectsConfiguration('editor.fontSize', { resource: URI.file('file1') }));
assert.ok(!testObject.affectsConfiguration('editor.fontSize', { resource: URI.file('file2') }));
});
test('changeEvent affecting tasks and launches', () => {
const configuration = new Configuration(new ConfigurationModel(), new ConfigurationModel());
const change = configuration.compareAndUpdateLocalUserConfiguration(toConfigurationModel({
'launch': {
'configuraiton': {}
},
'launch.version': 1,
'tasks': {
'version': 2
}
}));
let testObject = new ConfigurationChangeEvent(change, undefined, configuration);
assert.deepEqual(testObject.affectedKeys, ['launch', 'launch.version', 'tasks']);
assert.ok(testObject.affectsConfiguration('launch'));
assert.ok(testObject.affectsConfiguration('launch.version'));
assert.ok(testObject.affectsConfiguration('tasks'));
});
});
suite('AllKeysConfigurationChangeEvent', () => {
test('changeEvent', () => {
const configuration = new Configuration(new ConfigurationModel(), new ConfigurationModel());
configuration.updateDefaultConfiguration(toConfigurationModel({
'editor.lineNumbers': 'off',
'[markdown]': {
'editor.wordWrap': 'off'
}
}));
configuration.updateLocalUserConfiguration(toConfigurationModel({
'[json]': {
'editor.lineNumbers': 'relative'
}
}));
configuration.updateWorkspaceConfiguration(toConfigurationModel({ 'window.title': 'custom' }));
configuration.updateFolderConfiguration(URI.file('file1'), toConfigurationModel({ 'window.zoomLevel': 2, 'window.restoreFullscreen': true }));
configuration.updateFolderConfiguration(URI.file('file2'), toConfigurationModel({ 'workbench.editor.enablePreview': true, 'window.restoreWindows': true }));
const workspace = new Workspace('a', [new WorkspaceFolder({ index: 0, name: 'a', uri: URI.file('file1') }), new WorkspaceFolder({ index: 1, name: 'b', uri: URI.file('file2') }), new WorkspaceFolder({ index: 2, name: 'c', uri: URI.file('folder3') })]);
let testObject = new AllKeysConfigurationChangeEvent(configuration, workspace, ConfigurationTarget.USER, null);
assert.deepEqual(testObject.affectedKeys, ['editor.lineNumbers', '[markdown]', '[json]', 'window.title', 'window.zoomLevel', 'window.restoreFullscreen', 'workbench.editor.enablePreview', 'window.restoreWindows']);
assert.ok(testObject.affectsConfiguration('window.title'));
assert.ok(testObject.affectsConfiguration('window.title', { resource: URI.file('file1') }));
assert.ok(testObject.affectsConfiguration('window.title', { resource: URI.file('file2') }));
assert.ok(testObject.affectsConfiguration('window'));
assert.ok(testObject.affectsConfiguration('window', { resource: URI.file('file1') }));
assert.ok(testObject.affectsConfiguration('window', { resource: URI.file('file2') }));
assert.ok(testObject.affectsConfiguration('window.zoomLevel'));
assert.ok(testObject.affectsConfiguration('window.zoomLevel', { resource: URI.file('file1') }));
assert.ok(!testObject.affectsConfiguration('window.zoomLevel', { resource: URI.file('file2') }));
assert.ok(testObject.affectsConfiguration('window.restoreFullscreen'));
assert.ok(testObject.affectsConfiguration('window.restoreFullscreen', { resource: URI.file('file1') }));
assert.ok(!testObject.affectsConfiguration('window.restoreFullscreen', { resource: URI.file('file2') }));
assert.ok(testObject.affectsConfiguration('window.restoreWindows'));
assert.ok(testObject.affectsConfiguration('window.restoreWindows', { resource: URI.file('file2') }));
assert.ok(!testObject.affectsConfiguration('window.restoreWindows', { resource: URI.file('file1') }));
assert.ok(testObject.affectsConfiguration('workbench.editor.enablePreview'));
assert.ok(testObject.affectsConfiguration('workbench.editor.enablePreview', { resource: URI.file('file2') }));
assert.ok(!testObject.affectsConfiguration('workbench.editor.enablePreview', { resource: URI.file('file1') }));
assert.ok(testObject.affectsConfiguration('workbench.editor'));
assert.ok(testObject.affectsConfiguration('workbench.editor', { resource: URI.file('file2') }));
assert.ok(!testObject.affectsConfiguration('workbench.editor', { resource: URI.file('file1') }));
assert.ok(testObject.affectsConfiguration('workbench'));
assert.ok(testObject.affectsConfiguration('workbench', { resource: URI.file('file2') }));
assert.ok(!testObject.affectsConfiguration('workbench', { resource: URI.file('file1') }));
assert.ok(!testObject.affectsConfiguration('files'));
assert.ok(!testObject.affectsConfiguration('files', { resource: URI.file('file1') }));
assert.ok(!testObject.affectsConfiguration('files', { resource: URI.file('file2') }));
assert.ok(testObject.affectsConfiguration('editor'));
assert.ok(testObject.affectsConfiguration('editor', { resource: URI.file('file1') }));
assert.ok(testObject.affectsConfiguration('editor', { resource: URI.file('file2') }));
assert.ok(testObject.affectsConfiguration('editor', { resource: URI.file('file1'), overrideIdentifier: 'json' }));
assert.ok(testObject.affectsConfiguration('editor', { resource: URI.file('file1'), overrideIdentifier: 'markdown' }));
assert.ok(testObject.affectsConfiguration('editor', { resource: URI.file('file1'), overrideIdentifier: 'typescript' }));
assert.ok(testObject.affectsConfiguration('editor', { resource: URI.file('file2'), overrideIdentifier: 'json' }));
assert.ok(testObject.affectsConfiguration('editor', { resource: URI.file('file2'), overrideIdentifier: 'markdown' }));
assert.ok(testObject.affectsConfiguration('editor', { resource: URI.file('file2'), overrideIdentifier: 'typescript' }));
assert.ok(testObject.affectsConfiguration('editor.lineNumbers'));
assert.ok(testObject.affectsConfiguration('editor.lineNumbers', { resource: URI.file('file1') }));
assert.ok(testObject.affectsConfiguration('editor.lineNumbers', { resource: URI.file('file2') }));
assert.ok(testObject.affectsConfiguration('editor.lineNumbers', { resource: URI.file('file1'), overrideIdentifier: 'json' }));
assert.ok(testObject.affectsConfiguration('editor.lineNumbers', { resource: URI.file('file1'), overrideIdentifier: 'markdown' }));
assert.ok(testObject.affectsConfiguration('editor.lineNumbers', { resource: URI.file('file1'), overrideIdentifier: 'typescript' }));
assert.ok(testObject.affectsConfiguration('editor.lineNumbers', { resource: URI.file('file2'), overrideIdentifier: 'json' }));
assert.ok(testObject.affectsConfiguration('editor.lineNumbers', { resource: URI.file('file2'), overrideIdentifier: 'markdown' }));
assert.ok(testObject.affectsConfiguration('editor.lineNumbers', { resource: URI.file('file2'), overrideIdentifier: 'typescript' }));
assert.ok(!testObject.affectsConfiguration('editor.wordWrap'));
assert.ok(!testObject.affectsConfiguration('editor.wordWrap', { resource: URI.file('file1') }));
assert.ok(!testObject.affectsConfiguration('editor.wordWrap', { resource: URI.file('file2') }));
assert.ok(!testObject.affectsConfiguration('editor.wordWrap', { resource: URI.file('file1'), overrideIdentifier: 'json' }));
assert.ok(!testObject.affectsConfiguration('editor.wordWrap', { resource: URI.file('file1'), overrideIdentifier: 'markdown' }));
assert.ok(!testObject.affectsConfiguration('editor.wordWrap', { resource: URI.file('file1'), overrideIdentifier: 'typescript' }));
assert.ok(!testObject.affectsConfiguration('editor.wordWrap', { resource: URI.file('file2'), overrideIdentifier: 'json' }));
assert.ok(!testObject.affectsConfiguration('editor.wordWrap', { resource: URI.file('file2'), overrideIdentifier: 'markdown' }));
assert.ok(!testObject.affectsConfiguration('editor.wordWrap', { resource: URI.file('file2'), overrideIdentifier: 'typescript' }));
assert.ok(!testObject.affectsConfiguration('editor.fontSize'));
assert.ok(!testObject.affectsConfiguration('editor.fontSize', { resource: URI.file('file1') }));
assert.ok(!testObject.affectsConfiguration('editor.fontSize', { resource: URI.file('file2') }));
});
});
function toConfigurationModel(obj: any): ConfigurationModel {
const parser = new ConfigurationModelParser('test');
parser.parseContent(JSON.stringify(obj));
return parser.configurationModel;
}
@@ -5,12 +5,18 @@
import { TernarySearchTree } from 'vs/base/common/map';
import { URI } from 'vs/base/common/uri';
import { getConfigurationKeys, IConfigurationOverrides, IConfigurationService, getConfigurationValue, isConfigurationOverrides } from 'vs/platform/configuration/common/configuration';
import { getConfigurationKeys, IConfigurationOverrides, IConfigurationService, getConfigurationValue, isConfigurationOverrides, IConfigurationValue } from 'vs/platform/configuration/common/configuration';
import { Emitter } from 'vs/base/common/event';
export class TestConfigurationService implements IConfigurationService {
public _serviceBrand: undefined;
private configuration = Object.create(null);
private configuration: any;
readonly onDidChangeConfiguration = new Emitter<any>().event;
constructor(configuration?: any) {
this.configuration = configuration || Object.create(null);
}
private configurationByRoot: TernarySearchTree<any> = TernarySearchTree.forPaths<any>();
@@ -33,7 +39,7 @@ export class TestConfigurationService implements IConfigurationService {
return configuration;
}
public updateValue(key: string, overrides?: IConfigurationOverrides): Promise<void> {
public updateValue(key: string, value: any): Promise<void> {
return Promise.resolve(undefined);
}
@@ -49,27 +55,13 @@ export class TestConfigurationService implements IConfigurationService {
return Promise.resolve(undefined);
}
public onDidChangeConfiguration() {
return { dispose() { } };
}
public inspect<T>(key: string, overrides?: IConfigurationOverrides): {
default: T,
user: T,
userLocal?: T,
userRemote?: T,
workspace?: T,
workspaceFolder?: T
value: T,
} {
public inspect<T>(key: string, overrides?: IConfigurationOverrides): IConfigurationValue<T> {
const config = this.getValue(undefined, overrides);
return {
value: getConfigurationValue<T>(config, key),
default: getConfigurationValue<T>(config, key),
user: getConfigurationValue<T>(config, key),
workspace: undefined,
workspaceFolder: undefined
user: getConfigurationValue<T>(config, key)
};
}
+2
View File
@@ -32,6 +32,8 @@ export interface IssueReporterStyles extends WindowStyles {
inputForeground?: string;
inputBorder?: string;
inputErrorBorder?: string;
inputErrorBackground?: string;
inputErrorForeground?: string;
inputActiveBorder?: string;
buttonBackground?: string;
buttonForeground?: string;
@@ -9,8 +9,8 @@ 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 { getConfigurationValue } from 'vs/platform/configuration/common/configuration';
import { ITelemetryData } from 'vs/platform/telemetry/common/telemetry';
import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService';
class TestTelemetryAppender implements ITelemetryAppender {
@@ -729,30 +729,14 @@ suite('TelemetryService', () => {
let testAppender = new TestTelemetryAppender();
let service = new TelemetryService({
appender: testAppender
}, {
_serviceBrand: undefined,
}, new class extends TestConfigurationService {
onDidChangeConfiguration = emitter.event;
getValue() {
return {
enableTelemetry: enableTelemetry
} as any;
},
updateValue(): Promise<void> {
return null!;
},
inspect(key: string) {
return {
value: getConfigurationValue(this.getValue(), key),
default: getConfigurationValue(this.getValue(), key),
user: getConfigurationValue(this.getValue(), key),
workspace: null!,
workspaceFolder: null!
};
},
keys() { return { default: [], user: [], workspace: [], workspaceFolder: [] }; },
onDidChangeConfiguration: emitter.event,
reloadConfiguration(): Promise<void> { return null!; },
getConfigurationData() { return null; }
});
}
}());
assert.equal(service.isOptedIn, false);
@@ -12,14 +12,15 @@ import { RunOnceScheduler } from 'vs/base/common/async';
import { Event, Emitter } from 'vs/base/common/event';
import { IJSONSchema, IJSONSchemaMap } from 'vs/base/common/jsonSchema';
// ------ API types
export const TOKEN_TYPE_WILDCARD = '*';
export const TOKEN_TYPE_WILDCARD_NUM = -1;
// qualified string [type|*](.modifier)*
export type TokenClassificationString = string;
export const typeAndModifierIdPattern = '^\\w+[-_\\w+]*$';
export const fontStylePattern = '^(\\s*(-?italic|-?bold|-?underline))*\\s*$';
export interface TokenClassification {
type: number;
modifiers: number;
@@ -54,6 +55,34 @@ export namespace TokenStyle {
export function fromData(data: { foreground?: Color, bold?: boolean, underline?: boolean, italic?: boolean }) {
return new TokenStyle(data.foreground, data.bold, data.underline, data.italic);
}
export function fromSettings(foreground: string | undefined, fontStyle: string | undefined): 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 '-bold': bold = false; break;
case 'italic': italic = true; break;
case '-italic': italic = false; break;
case 'underline': underline = true; break;
case '-underline': underline = false; break;
}
}
}
}
return new TokenStyle(foregroundColor, bold, underline, italic);
}
}
export type ProbeScope = string[];
@@ -63,10 +92,10 @@ export interface TokenStyleFunction {
}
export interface TokenStyleDefaults {
scopesToProbe: ProbeScope[];
light: TokenStyleValue | null;
dark: TokenStyleValue | null;
hc: TokenStyleValue | null;
scopesToProbe?: ProbeScope[];
light?: TokenStyleValue;
dark?: TokenStyleValue;
hc?: TokenStyleValue;
}
export interface TokenStylingDefaultRule {
@@ -120,6 +149,12 @@ export interface ITokenClassificationRegistry {
*/
registerTokenStyleDefault(selector: TokenClassification, defaults: TokenStyleDefaults): void;
/**
* Deregister a TokenStyle default to the registry.
* @param selector The rule selector
*/
deregisterTokenStyleDefault(selector: TokenClassification): void;
/**
* Deregister a TokenType from the registry.
*/
@@ -186,7 +221,7 @@ class TokenClassificationRegistry implements ITokenClassificationRegistry {
fontStyle: {
type: 'string',
description: nls.localize('schema.token.fontStyle', 'Font style of the rule: \'italic\', \'bold\' or \'underline\', \'-italic\', \'-bold\' or \'-underline\'or a combination. The empty string unsets inherited settings.'),
pattern: '^(\\s*(-?italic|-?bold|-?underline))*\\s*$',
pattern: fontStylePattern,
patternErrorMessage: nls.localize('schema.fontStyle.error', 'Font style must be \'italic\', \'bold\' or \'underline\' to set a style or \'-italic\', \'-bold\' or \'-underline\' to unset 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' }, { body: '-bold' }, { body: '-underline' }, { body: 'italic bold' }, { body: 'italic underline' }, { body: 'bold underline' }, { body: 'italic bold underline' }]
}
@@ -205,6 +240,10 @@ class TokenClassificationRegistry implements ITokenClassificationRegistry {
}
public registerTokenType(id: string, description: string, deprecationMessage?: string): void {
if (!id.match(typeAndModifierIdPattern)) {
throw new Error('Invalid token type id.');
}
const num = this.currentTypeNumber++;
let tokenStyleContribution: TokenTypeOrModifierContribution = { num, id, description, deprecationMessage };
this.tokenTypeById[id] = tokenStyleContribution;
@@ -213,6 +252,10 @@ class TokenClassificationRegistry implements ITokenClassificationRegistry {
}
public registerTokenModifier(id: string, description: string, deprecationMessage?: string): void {
if (!id.match(typeAndModifierIdPattern)) {
throw new Error('Invalid token modifier id.');
}
const num = this.currentModifierBit;
this.currentModifierBit = this.currentModifierBit * 2;
let tokenStyleContribution: TokenTypeOrModifierContribution = { num, id, description, deprecationMessage };
@@ -244,6 +287,10 @@ class TokenClassificationRegistry implements ITokenClassificationRegistry {
this.tokenStylingDefaultRules.push({ classification, matchScore: getTokenStylingScore(classification), defaults });
}
public deregisterTokenStyleDefault(classification: TokenClassification): void {
this.tokenStylingDefaultRules = this.tokenStylingDefaultRules.filter(r => !(r.classification.type === classification.type && r.classification.modifiers === classification.modifiers));
}
public deregisterTokenType(id: string): void {
delete this.tokenTypeById[id];
delete this.tokenStylingSchema.properties[id];
@@ -270,6 +317,7 @@ class TokenClassificationRegistry implements ITokenClassificationRegistry {
return this.tokenStylingDefaultRules;
}
public toString() {
let sorter = (a: string, b: string) => {
let cat1 = a.indexOf('.') === -1 ? 0 : 1;
@@ -301,65 +349,63 @@ export function matchTokenStylingRule(themeSelector: TokenStylingRule | TokenSty
const tokenClassificationRegistry = new TokenClassificationRegistry();
platform.Registry.add(Extensions.TokenClassificationContribution, tokenClassificationRegistry);
export function registerTokenType(id: string, description: string, scopesToProbe: ProbeScope[] = [], extendsTC: string | null = null, deprecationMessage?: string): string {
tokenClassificationRegistry.registerTokenType(id, description, deprecationMessage);
registerDefaultClassifications();
if (scopesToProbe || extendsTC) {
const classification = tokenClassificationRegistry.getTokenClassification(id, []);
tokenClassificationRegistry.registerTokenStyleDefault(classification!, { scopesToProbe, light: extendsTC, dark: extendsTC, hc: extendsTC });
function registerDefaultClassifications(): void {
function registerTokenType(id: string, description: string, scopesToProbe: ProbeScope[] = [], extendsTC?: string, deprecationMessage?: string): string {
tokenClassificationRegistry.registerTokenType(id, description, deprecationMessage);
if (scopesToProbe || extendsTC) {
const classification = tokenClassificationRegistry.getTokenClassification(id, []);
tokenClassificationRegistry.registerTokenStyleDefault(classification!, { scopesToProbe, light: extendsTC, dark: extendsTC, hc: extendsTC });
}
return id;
}
return id;
}
export function registerTokenModifier(id: string, description: string, deprecationMessage?: string): string {
tokenClassificationRegistry.registerTokenModifier(id, description, deprecationMessage);
return id;
// default token types
registerTokenType('comment', nls.localize('comment', "Style for comments."), [['comment']]);
registerTokenType('string', nls.localize('string', "Style for strings."), [['string']]);
registerTokenType('keyword', nls.localize('keyword', "Style for keywords."), [['keyword.control']]);
registerTokenType('number', nls.localize('number', "Style for numbers."), [['constant.numeric']]);
registerTokenType('regexp', nls.localize('regexp', "Style for expressions."), [['constant.regexp']]);
registerTokenType('operator', nls.localize('operator', "Style for operators."), [['keyword.operator']]);
registerTokenType('namespace', nls.localize('namespace', "Style for namespaces."), [['entity.name.namespace']]);
registerTokenType('type', nls.localize('type', "Style for types."), [['entity.name.type'], ['entity.name.class'], ['support.type'], ['support.class']]);
registerTokenType('struct', nls.localize('struct', "Style for structs."), [['storage.type.struct']], 'type');
registerTokenType('class', nls.localize('class', "Style for classes."), [['entity.name.class']], 'type');
registerTokenType('interface', nls.localize('interface', "Style for interfaces."), undefined, 'type');
registerTokenType('enum', nls.localize('enum', "Style for enums."), undefined, 'type');
registerTokenType('parameterType', nls.localize('parameterType', "Style for parameter types."), undefined, 'type');
registerTokenType('function', nls.localize('function', "Style for functions"), [['entity.name.function'], ['support.function']]);
registerTokenType('macro', nls.localize('macro', "Style for macros."), undefined, 'function');
registerTokenType('variable', nls.localize('variable', "Style for variables."), [['variable'], ['entity.name.variable']]);
registerTokenType('constant', nls.localize('constant', "Style for constants."), undefined, 'variable');
registerTokenType('parameter', nls.localize('parameter', "Style for parameters."), undefined, 'variable');
registerTokenType('property', nls.localize('propertie', "Style for properties."), undefined, 'variable');
registerTokenType('label', nls.localize('labels', "Style for labels. "), undefined);
// default token modifiers
tokenClassificationRegistry.registerTokenModifier('declaration', nls.localize('declaration', "Style for all symbol declarations."), undefined);
tokenClassificationRegistry.registerTokenModifier('documentation', nls.localize('documentation', "Style to use for references in documentation."), undefined);
tokenClassificationRegistry.registerTokenModifier('member', nls.localize('member', "Style to use for member functions, variables (fields) and types."), undefined);
tokenClassificationRegistry.registerTokenModifier('static', nls.localize('static', "Style to use for symbols that are static."), undefined);
tokenClassificationRegistry.registerTokenModifier('abstract', nls.localize('abstract', "Style to use for symbols that are abstract."), undefined);
tokenClassificationRegistry.registerTokenModifier('deprecated', nls.localize('deprecated', "Style to use for symbols that are deprecated."), undefined);
tokenClassificationRegistry.registerTokenModifier('modification', nls.localize('modification', "Style to use for write accesses."), undefined);
tokenClassificationRegistry.registerTokenModifier('async', nls.localize('async', "Style to use for symbols that are async."), undefined);
}
export function getTokenClassificationRegistry(): ITokenClassificationRegistry {
return tokenClassificationRegistry;
}
// default token types
registerTokenType('comment', nls.localize('comment', "Style for comments."), [['comment']]);
registerTokenType('string', nls.localize('string', "Style for strings."), [['string']]);
registerTokenType('keyword', nls.localize('keyword', "Style for keywords."), [['keyword.control']]);
registerTokenType('number', nls.localize('number', "Style for numbers."), [['constant.numeric']]);
registerTokenType('regexp', nls.localize('regexp', "Style for expressions."), [['constant.regexp']]);
registerTokenType('operator', nls.localize('operator', "Style for operators."), [['keyword.operator']]);
registerTokenType('namespace', nls.localize('namespace', "Style for namespaces."), [['entity.name.namespace']]);
registerTokenType('type', nls.localize('type', "Style for types."), [['entity.name.type'], ['entity.name.class'], ['support.type'], ['support.class']]);
registerTokenType('struct', nls.localize('struct', "Style for structs."), [['storage.type.struct']], 'type');
registerTokenType('class', nls.localize('class', "Style for classes."), [['entity.name.class']], 'type');
registerTokenType('interface', nls.localize('interface', "Style for interfaces."), undefined, 'type');
registerTokenType('enum', nls.localize('enum', "Style for enums."), undefined, 'type');
registerTokenType('parameterType', nls.localize('parameterType', "Style for parameter types."), undefined, 'type');
registerTokenType('function', nls.localize('function', "Style for functions"), [['entity.name.function'], ['support.function']]);
registerTokenType('macro', nls.localize('macro', "Style for macros."), undefined, 'function');
registerTokenType('variable', nls.localize('variable', "Style for variables."), [['variable'], ['entity.name.variable']]);
registerTokenType('constant', nls.localize('constant', "Style for constants."), undefined, 'variable');
registerTokenType('parameter', nls.localize('parameter', "Style for parameters."), undefined, 'variable');
registerTokenType('property', nls.localize('propertie', "Style for properties."), undefined, 'variable');
registerTokenType('label', nls.localize('labels', "Style for labels. "), undefined);
// default token modifiers
registerTokenModifier('declaration', nls.localize('declaration', "Style for all symbol declarations."), undefined);
registerTokenModifier('documentation', nls.localize('documentation', "Style to use for references in documentation."), undefined);
registerTokenModifier('member', nls.localize('member', "Style to use for member functions, variables (fields) and types."), undefined);
registerTokenModifier('static', nls.localize('static', "Style to use for symbols that are static."), undefined);
registerTokenModifier('abstract', nls.localize('abstract', "Style to use for symbols that are abstract."), undefined);
registerTokenModifier('deprecated', nls.localize('deprecated', "Style to use for symbols that are deprecated."), undefined);
registerTokenModifier('modification', nls.localize('modification', "Style to use for write accesses."), undefined);
registerTokenModifier('async', nls.localize('async', "Style to use for symbols that are async."), undefined);
function bitCount(u: number) {
// https://blogs.msdn.microsoft.com/jeuge/2005/06/08/bit-fiddling-3/
const uCount = u - ((u >> 1) & 0o33333333333) - ((u >> 2) & 0o11111111111);
@@ -326,7 +326,9 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic
}
// Persist
this.stateService.setItem(WindowsMainService.windowsStateStorageKey, getWindowsStateStoreData(currentWindowsState));
const state = getWindowsStateStoreData(currentWindowsState);
this.logService.trace('onBeforeShutdown', state);
this.stateService.setItem(WindowsMainService.windowsStateStorageKey, state);
}
// See note on #onBeforeShutdown() for details how these events are flowing
+2 -2
View File
@@ -797,10 +797,10 @@ declare module 'vscode' {
export interface DebugAdapter extends Disposable {
/**
* An event which fires when the debug adapter sends a Debug Adapter Protocol message to VS Code.
* An event which fires after the debug adapter has sent a Debug Adapter Protocol message to VS Code.
* Messages can be requests, responses, or events.
*/
readonly onSendMessage: Event<DebugProtocolMessage>;
readonly onDidSendMessage: Event<DebugProtocolMessage>;
/**
* Handle a Debug Adapter Protocol message.
@@ -11,6 +11,7 @@ import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle';
// --- other interested parties
import { JSONValidationExtensionPoint } from 'vs/workbench/api/common/jsonValidationExtensionPoint';
import { ColorExtensionPoint } from 'vs/workbench/services/themes/common/colorExtensionPoint';
import { TokenClassificationExtensionPoints } from 'vs/workbench/services/themes/common/tokenClassificationExtensionPoint';
import { LanguageConfigurationFileHandler } from 'vs/workbench/contrib/codeEditor/browser/languageConfigurationExtensionPoint';
// --- mainThread participants
@@ -67,6 +68,7 @@ export class ExtensionPoints implements IWorkbenchContribution {
// Classes that handle extension points...
this.instantiationService.createInstance(JSONValidationExtensionPoint);
this.instantiationService.createInstance(ColorExtensionPoint);
this.instantiationService.createInstance(TokenClassificationExtensionPoints);
this.instantiationService.createInstance(LanguageConfigurationFileHandler);
}
}
@@ -8,9 +8,9 @@ import { IDisposable } from 'vs/base/common/lifecycle';
import { Registry } from 'vs/platform/registry/common/platform';
import { IConfigurationRegistry, Extensions as ConfigurationExtensions, ConfigurationScope, getScopes } from 'vs/platform/configuration/common/configurationRegistry';
import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace';
import { MainThreadConfigurationShape, MainContext, ExtHostContext, IExtHostContext, IWorkspaceConfigurationChangeEventData, IConfigurationInitData } from '../common/extHost.protocol';
import { MainThreadConfigurationShape, MainContext, ExtHostContext, IExtHostContext, IConfigurationInitData } from '../common/extHost.protocol';
import { extHostNamedCustomer } from 'vs/workbench/api/common/extHostCustomers';
import { ConfigurationTarget, IConfigurationChangeEvent, IConfigurationModel, IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { ConfigurationTarget, IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
@extHostNamedCustomer(MainContext.MainThreadConfiguration)
@@ -28,7 +28,7 @@ export class MainThreadConfiguration implements MainThreadConfigurationShape {
proxy.$initializeConfiguration(this._getConfigurationData());
this._configurationListener = configurationService.onDidChangeConfiguration(e => {
proxy.$acceptConfigurationChanged(this._getConfigurationData(), this.toConfigurationChangeEventData(e));
proxy.$acceptConfigurationChanged(this._getConfigurationData(), e.change);
});
}
@@ -69,22 +69,4 @@ export class MainThreadConfiguration implements MainThreadConfigurationShape {
}
return ConfigurationTarget.WORKSPACE;
}
private toConfigurationChangeEventData(event: IConfigurationChangeEvent): IWorkspaceConfigurationChangeEventData {
return {
changedConfiguration: this.toJSONConfiguration(event.changedConfiguration),
changedConfigurationByResource: event.changedConfigurationByResource.keys().reduce((result, resource) => {
result[resource.toString()] = this.toJSONConfiguration(event.changedConfigurationByResource.get(resource));
return result;
}, Object.create({}))
};
}
private toJSONConfiguration({ contents, keys, overrides }: IConfigurationModel = { contents: {}, keys: [], overrides: [] }): IConfigurationModel {
return {
contents,
keys,
overrides
};
}
}
@@ -10,7 +10,6 @@ import { IRemoteExplorerService } from 'vs/workbench/services/remote/common/remo
@extHostNamedCustomer(MainContext.MainThreadTunnelService)
export class MainThreadTunnelService implements MainThreadTunnelServiceShape {
// @ts-ignore
private readonly _proxy: ExtHostTunnelServiceShape;
constructor(
@@ -36,6 +35,10 @@ export class MainThreadTunnelService implements MainThreadTunnelServiceShape {
return Promise.resolve(this.remoteExplorerService.addDetected(tunnels));
}
async $registerCandidateFinder(): Promise<void> {
this.remoteExplorerService.registerCandidateFinder(() => this._proxy.$findCandidatePorts());
}
dispose(): void {
//
}
@@ -22,7 +22,7 @@ import { IModelChangedEvent } from 'vs/editor/common/model/mirrorTextModel';
import * as modes from 'vs/editor/common/modes';
import { CharacterPair, CommentRule, EnterAction } from 'vs/editor/common/modes/languageConfiguration';
import { ICommandHandlerDescription } from 'vs/platform/commands/common/commands';
import { ConfigurationTarget, IConfigurationData, IConfigurationModel } from 'vs/platform/configuration/common/configuration';
import { ConfigurationTarget, IConfigurationData, IConfigurationChange } from 'vs/platform/configuration/common/configuration';
import { ConfigurationScope } from 'vs/platform/configuration/common/configurationRegistry';
import { ExtensionIdentifier, IExtensionDescription } from 'vs/platform/extensions/common/extensions';
import * as files from 'vs/platform/files/common/files';
@@ -101,11 +101,6 @@ export interface IConfigurationInitData extends IConfigurationData {
configurationScopes: [string, ConfigurationScope | undefined][];
}
export interface IWorkspaceConfigurationChangeEventData {
changedConfiguration: IConfigurationModel;
changedConfigurationByResource: { [folder: string]: IConfigurationModel; };
}
export interface IExtHostContext extends IRPCProtocol {
remoteAuthority: string;
}
@@ -786,6 +781,7 @@ export interface MainThreadTunnelServiceShape extends IDisposable {
$openTunnel(tunnelOptions: TunnelOptions): Promise<TunnelDto | undefined>;
$closeTunnel(remotePort: number): Promise<void>;
$addDetected(tunnels: { remote: { port: number, host: string }, localAddress: string }[]): Promise<void>;
$registerCandidateFinder(): Promise<void>;
}
// -- extension host
@@ -797,7 +793,7 @@ export interface ExtHostCommandsShape {
export interface ExtHostConfigurationShape {
$initializeConfiguration(data: IConfigurationInitData): void;
$acceptConfigurationChanged(data: IConfigurationInitData, eventData: IWorkspaceConfigurationChangeEventData): void;
$acceptConfigurationChanged(data: IConfigurationInitData, change: IConfigurationChange): void;
}
export interface ExtHostDiagnosticsShape {
@@ -1404,7 +1400,7 @@ export interface ExtHostStorageShape {
export interface ExtHostTunnelServiceShape {
$findCandidatePorts(): Promise<{ port: number, detail: string }[]>;
}
// --- proxy identifiers
@@ -8,12 +8,10 @@ import { URI } from 'vs/base/common/uri';
import { Event, Emitter } from 'vs/base/common/event';
import * as vscode from 'vscode';
import { ExtHostWorkspace, IExtHostWorkspace } from 'vs/workbench/api/common/extHostWorkspace';
import { ExtHostConfigurationShape, MainThreadConfigurationShape, IWorkspaceConfigurationChangeEventData, IConfigurationInitData, MainContext } from './extHost.protocol';
import { ExtHostConfigurationShape, MainThreadConfigurationShape, IConfigurationInitData, MainContext } from './extHost.protocol';
import { ConfigurationTarget as ExtHostConfigurationTarget } from './extHostTypes';
import { IConfigurationData, ConfigurationTarget, IConfigurationModel } from 'vs/platform/configuration/common/configuration';
import { Configuration, ConfigurationChangeEvent, ConfigurationModel } from 'vs/platform/configuration/common/configurationModels';
import { WorkspaceConfigurationChangeEvent } from 'vs/workbench/services/configuration/common/configurationModels';
import { ResourceMap } from 'vs/base/common/map';
import { ConfigurationTarget, IConfigurationChange, IConfigurationData } from 'vs/platform/configuration/common/configuration';
import { Configuration, ConfigurationChangeEvent } from 'vs/platform/configuration/common/configurationModels';
import { ConfigurationScope, OVERRIDE_PROPERTY_PATTERN } from 'vs/platform/configuration/common/configurationRegistry';
import { isObject } from 'vs/base/common/types';
import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions';
@@ -21,6 +19,7 @@ import { Barrier } from 'vs/base/common/async';
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
import { IExtHostRpcService } from 'vs/workbench/api/common/extHostRpcService';
import { ILogService } from 'vs/platform/log/common/log';
import { Workspace } from 'vs/platform/workspace/common/workspace';
function lookUp(tree: any, key: string) {
if (key) {
@@ -72,8 +71,8 @@ export class ExtHostConfiguration implements ExtHostConfigurationShape {
this._barrier.open();
}
$acceptConfigurationChanged(data: IConfigurationInitData, eventData: IWorkspaceConfigurationChangeEventData): void {
this.getConfigProvider().then(provider => provider.$acceptConfigurationChanged(data, eventData));
$acceptConfigurationChanged(data: IConfigurationInitData, change: IConfigurationChange): void {
this.getConfigProvider().then(provider => provider.$acceptConfigurationChanged(data, change));
}
}
@@ -90,7 +89,7 @@ export class ExtHostConfigProvider {
this._proxy = proxy;
this._logService = logService;
this._extHostWorkspace = extHostWorkspace;
this._configuration = ExtHostConfigProvider.parse(data);
this._configuration = Configuration.parse(data);
this._configurationScopes = this._toMap(data.configurationScopes);
}
@@ -98,10 +97,11 @@ export class ExtHostConfigProvider {
return this._onDidChangeConfiguration && this._onDidChangeConfiguration.event;
}
$acceptConfigurationChanged(data: IConfigurationInitData, eventData: IWorkspaceConfigurationChangeEventData) {
this._configuration = ExtHostConfigProvider.parse(data);
$acceptConfigurationChanged(data: IConfigurationInitData, change: IConfigurationChange) {
const previous = { data: this._configuration.toData(), workspace: this._extHostWorkspace.workspace };
this._configuration = Configuration.parse(data);
this._configurationScopes = this._toMap(data.configurationScopes);
this._onDidChangeConfiguration.fire(this._toConfigurationChangeEvent(eventData));
this._onDidChangeConfiguration.fire(this._toConfigurationChangeEvent(change, previous));
}
getConfiguration(section?: string, resource?: URI, extensionId?: ExtensionIdentifier): vscode.WorkspaceConfiguration {
@@ -254,17 +254,10 @@ export class ExtHostConfigProvider {
}
}
private _toConfigurationChangeEvent(data: IWorkspaceConfigurationChangeEventData): vscode.ConfigurationChangeEvent {
const changedConfiguration = new ConfigurationModel(data.changedConfiguration.contents, data.changedConfiguration.keys, data.changedConfiguration.overrides);
const changedConfigurationByResource: ResourceMap<ConfigurationModel> = new ResourceMap<ConfigurationModel>();
for (const key of Object.keys(data.changedConfigurationByResource)) {
const resource = URI.parse(key);
const model = data.changedConfigurationByResource[key];
changedConfigurationByResource.set(resource, new ConfigurationModel(model.contents, model.keys, model.overrides));
}
const event = new WorkspaceConfigurationChangeEvent(new ConfigurationChangeEvent(changedConfiguration, changedConfigurationByResource), this._extHostWorkspace.workspace);
private _toConfigurationChangeEvent(change: IConfigurationChange, previous: { data: IConfigurationData, workspace: Workspace | undefined }): vscode.ConfigurationChangeEvent {
const event = new ConfigurationChangeEvent(change, previous, this._configuration, this._extHostWorkspace.workspace);
return Object.freeze({
affectsConfiguration: (section: string, resource?: URI) => event.affectsConfiguration(section, resource)
affectsConfiguration: (section: string, resource?: URI) => event.affectsConfiguration(section, resource ? { resource } : undefined)
});
}
@@ -272,20 +265,6 @@ export class ExtHostConfigProvider {
return scopes.reduce((result, scope) => { result.set(scope[0], scope[1]); return result; }, new Map<string, ConfigurationScope | undefined>());
}
private static parse(data: IConfigurationData): Configuration {
const defaultConfiguration = ExtHostConfigProvider.parseConfigurationModel(data.defaults);
const userConfiguration = ExtHostConfigProvider.parseConfigurationModel(data.user);
const workspaceConfiguration = ExtHostConfigProvider.parseConfigurationModel(data.workspace);
const folders: ResourceMap<ConfigurationModel> = data.folders.reduce((result, value) => {
result.set(URI.revive(value[0]), ExtHostConfigProvider.parseConfigurationModel(value[1]));
return result;
}, new ResourceMap<ConfigurationModel>());
return new Configuration(defaultConfiguration, userConfiguration, new ConfigurationModel(), workspaceConfiguration, folders, new ConfigurationModel(), new ResourceMap<ConfigurationModel>(), false);
}
private static parseConfigurationModel(model: IConfigurationModel): ConfigurationModel {
return new ConfigurationModel(model.contents, model.keys, model.overrides).freeze();
}
}
export const IExtHostConfiguration = createDecorator<IExtHostConfiguration>('IExtHostConfiguration');
@@ -825,7 +825,7 @@ export class ExtHostDebugServiceBase implements IExtHostDebugService, ExtHostDeb
if (aex) {
const folder = session.workspaceFolder;
const rootFolder = folder ? folder.uri.toString() : undefined;
return this._commandService.executeCommand(aex, rootFolder).then((ae: { command: string, args: string[] }) => {
return this._commandService.executeCommand(aex, rootFolder).then((ae: any) => {
return new DebugAdapterExecutable(ae.command, ae.args || []);
});
}
@@ -1049,9 +1049,9 @@ class DirectDebugAdapter extends AbstractDebugAdapter {
constructor(private implementation: vscode.DebugAdapter) {
super();
if (this.implementation.onSendMessage) {
implementation.onSendMessage((message: DebugProtocol.ProtocolMessage) => {
this.acceptMessage(message);
if (this.implementation.onDidSendMessage) {
implementation.onDidSendMessage((message: vscode.DebugProtocolMessage) => {
this.acceptMessage(message as DebugProtocol.ProtocolMessage);
});
}
}
@@ -3,11 +3,9 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { ExtHostTunnelServiceShape, MainThreadTunnelServiceShape, MainContext } from 'vs/workbench/api/common/extHost.protocol';
import { ExtHostTunnelServiceShape } from 'vs/workbench/api/common/extHost.protocol';
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
import { IExtHostRpcService } from 'vs/workbench/api/common/extHostRpcService';
import * as vscode from 'vscode';
import { Disposable } from 'vs/base/common/lifecycle';
export interface TunnelOptions {
remote: { port: number, host: string };
@@ -28,39 +26,3 @@ export interface IExtHostTunnelService extends ExtHostTunnelServiceShape {
}
export const IExtHostTunnelService = createDecorator<IExtHostTunnelService>('IExtHostTunnelService');
export class ExtHostTunnelService extends Disposable implements IExtHostTunnelService {
readonly _serviceBrand: undefined;
private readonly _proxy: MainThreadTunnelServiceShape;
constructor(
@IExtHostRpcService extHostRpc: IExtHostRpcService
) {
super();
this._proxy = extHostRpc.getProxy(MainContext.MainThreadTunnelService);
}
async makeTunnel(forward: TunnelOptions): Promise<vscode.Tunnel | undefined> {
const tunnel = await this._proxy.$openTunnel(forward);
if (tunnel) {
const disposableTunnel: vscode.Tunnel = {
remote: tunnel.remote,
localAddress: tunnel.localAddress,
dispose: () => {
return this._proxy.$closeTunnel(tunnel.remote.port);
}
};
this._register(disposableTunnel);
return disposableTunnel;
}
return undefined;
}
async addDetected(tunnels: { remote: { port: number, host: string }, localAddress: string }[] | undefined): Promise<void> {
if (tunnels) {
return this._proxy.$addDetected(tunnels);
}
}
}
@@ -26,7 +26,8 @@ import { ExtHostExtensionService } from 'vs/workbench/api/node/extHostExtensionS
import { IExtHostStorage, ExtHostStorage } from 'vs/workbench/api/common/extHostStorage';
import { ILogService } from 'vs/platform/log/common/log';
import { ExtHostLogService } from 'vs/workbench/api/node/extHostLogService';
import { IExtHostTunnelService, ExtHostTunnelService } from 'vs/workbench/api/common/extHostTunnelService';
import { IExtHostTunnelService } from 'vs/workbench/api/common/extHostTunnelService';
import { ExtHostTunnelService } from 'vs/workbench/api/node/extHostTunnelService';
// register singleton services
registerSingleton(ILogService, ExtHostLogService);
@@ -0,0 +1,149 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { MainThreadTunnelServiceShape, MainContext } from 'vs/workbench/api/common/extHost.protocol';
import { IExtHostRpcService } from 'vs/workbench/api/common/extHostRpcService';
import * as vscode from 'vscode';
import { Disposable } from 'vs/base/common/lifecycle';
import { IExtHostInitDataService } from 'vs/workbench/api/common/extHostInitDataService';
import { URI } from 'vs/base/common/uri';
import { exec } from 'child_process';
import * as resources from 'vs/base/common/resources';
import * as fs from 'fs';
import { isLinux } from 'vs/base/common/platform';
import { IExtHostTunnelService, TunnelOptions } from 'vs/workbench/api/common/extHostTunnelService';
export class ExtHostTunnelService extends Disposable implements IExtHostTunnelService {
readonly _serviceBrand: undefined;
private readonly _proxy: MainThreadTunnelServiceShape;
constructor(
@IExtHostRpcService extHostRpc: IExtHostRpcService,
@IExtHostInitDataService initData: IExtHostInitDataService
) {
super();
this._proxy = extHostRpc.getProxy(MainContext.MainThreadTunnelService);
if (initData.remote.isRemote && initData.remote.authority) {
this.registerCandidateFinder();
}
}
async makeTunnel(forward: TunnelOptions): Promise<vscode.Tunnel | undefined> {
const tunnel = await this._proxy.$openTunnel(forward);
if (tunnel) {
const disposableTunnel: vscode.Tunnel = {
remote: tunnel.remote,
localAddress: tunnel.localAddress,
dispose: () => {
return this._proxy.$closeTunnel(tunnel.remote.port);
}
};
this._register(disposableTunnel);
return disposableTunnel;
}
return undefined;
}
async addDetected(tunnels: { remote: { port: number, host: string }, localAddress: string }[] | undefined): Promise<void> {
if (tunnels) {
return this._proxy.$addDetected(tunnels);
}
}
registerCandidateFinder(): Promise<void> {
return this._proxy.$registerCandidateFinder();
}
async $findCandidatePorts(): Promise<{ port: number, detail: string }[]> {
if (!isLinux) {
return [];
}
const ports: { port: number, detail: string }[] = [];
const tcp: string = fs.readFileSync('/proc/net/tcp', 'utf8');
const tcp6: string = fs.readFileSync('/proc/net/tcp6', 'utf8');
const procSockets: string = await (new Promise(resolve => {
exec('ls -l /proc/[0-9]*/fd/[0-9]* | grep socket:', (error, stdout, stderr) => {
resolve(stdout);
});
}));
const procChildren = fs.readdirSync('/proc');
const processes: { pid: number, cwd: string, cmd: string }[] = [];
for (let childName of procChildren) {
try {
const pid: number = Number(childName);
const childUri = resources.joinPath(URI.file('/proc'), childName);
const childStat = fs.statSync(childUri.fsPath);
if (childStat.isDirectory() && !isNaN(pid)) {
const cwd = fs.readlinkSync(resources.joinPath(childUri, 'cwd').fsPath);
const cmd = fs.readFileSync(resources.joinPath(childUri, 'cmdline').fsPath, 'utf8').replace(/\0/g, ' ');
processes.push({ pid, cwd, cmd });
}
} catch (e) {
//
}
}
const connections: { socket: number, ip: string, port: number }[] = this.loadListeningPorts(tcp, tcp6);
const sockets = this.getSockets(procSockets);
const socketMap = sockets.reduce((m, socket) => {
m[socket.socket] = socket;
return m;
}, {} as Record<string, typeof sockets[0]>);
const processMap = processes.reduce((m, process) => {
m[process.pid] = process;
return m;
}, {} as Record<string, typeof processes[0]>);
connections.filter((connection => socketMap[connection.socket])).forEach(({ socket, ip, port }) => {
const command = processMap[socketMap[socket].pid].cmd;
if (!command.match('.*\.vscode\-server\-[a-zA-Z]+\/bin.*') && (command.indexOf('out/vs/server/main.js') === -1)) {
ports.push({ port, detail: processMap[socketMap[socket].pid].cmd });
}
});
return ports;
}
private getSockets(stdout: string) {
const lines = stdout.trim().split('\n');
return lines.map(line => {
const match = /\/proc\/(\d+)\/fd\/\d+ -> socket:\[(\d+)\]/.exec(line)!;
return {
pid: parseInt(match[1], 10),
socket: parseInt(match[2], 10)
};
});
}
private loadListeningPorts(...stdouts: string[]): { socket: number, ip: string, port: number }[] {
const table = ([] as Record<string, string>[]).concat(...stdouts.map(this.loadConnectionTable));
return [
...new Map(
table.filter(row => row.st === '0A')
.map(row => {
const address = row.local_address.split(':');
return {
socket: parseInt(row.inode, 10),
ip: address[0],
port: parseInt(address[1], 16)
};
}).map(port => [port.port, port])
).values()
];
}
private loadConnectionTable(stdout: string): Record<string, string>[] {
const lines = stdout.trim().split('\n');
const names = lines.shift()!.trim().split(/\s+/)
.filter(name => name !== 'rx_queue' && name !== 'tm->when');
const table = lines.map(line => line.trim().split(/\s+/).reduce((obj, value, i) => {
obj[names[i] || i] = value;
return obj;
}, {} as Record<string, string>));
return table;
}
}
+1 -1
View File
@@ -542,7 +542,7 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi
}
// Empty workbench
else if (this.contextService.getWorkbenchState() === WorkbenchState.EMPTY && this.configurationService.inspect('workbench.startupEditor').value === 'newUntitledFile') {
else if (this.contextService.getWorkbenchState() === WorkbenchState.EMPTY && this.configurationService.getValue('workbench.startupEditor') === 'newUntitledFile') {
if (this.editorGroupService.willRestoreEditors) {
return []; // do not open any empty untitled file if we restored editors from previous session
}
@@ -47,7 +47,7 @@ import { IEditorGroupView } from 'vs/workbench/browser/parts/editor/editor';
import { onDidChangeZoomLevel } from 'vs/base/browser/browser';
import { withNullAsUndefined, withUndefinedAsNull } from 'vs/base/common/types';
import { ILabelService } from 'vs/platform/label/common/label';
import { ITextResourceConfigurationService } from 'vs/editor/common/services/resourceConfiguration';
import { IResourceConfigurationService } from 'vs/editor/common/services/resourceConfiguration';
class Item extends BreadcrumbsItem {
@@ -169,7 +169,7 @@ export class BreadcrumbsControl {
@IThemeService private readonly _themeService: IThemeService,
@IQuickOpenService private readonly _quickOpenService: IQuickOpenService,
@IConfigurationService private readonly _configurationService: IConfigurationService,
@ITextResourceConfigurationService private readonly _textResourceConfigurationService: ITextResourceConfigurationService,
@IResourceConfigurationService private readonly _textResourceConfigurationService: IResourceConfigurationService,
@IFileService private readonly _fileService: IFileService,
@ITelemetryService private readonly _telemetryService: ITelemetryService,
@ILabelService private readonly _labelService: ILabelService,
@@ -24,7 +24,7 @@ import { FileKind } from 'vs/platform/files/common/files';
import { withNullAsUndefined } from 'vs/base/common/types';
import { OutlineFilter } from 'vs/editor/contrib/documentSymbols/outlineTree';
import { ITextModel } from 'vs/editor/common/model';
import { ITextResourceConfigurationService } from 'vs/editor/common/services/resourceConfiguration';
import { IResourceConfigurationService } from 'vs/editor/common/services/resourceConfiguration';
export class FileElement {
constructor(
@@ -55,7 +55,7 @@ export class EditorBreadcrumbsModel {
private readonly _uri: URI,
private readonly _editor: ICodeEditor | undefined,
@IConfigurationService private readonly _configurationService: IConfigurationService,
@ITextResourceConfigurationService private readonly _textResourceConfigurationService: ITextResourceConfigurationService,
@IResourceConfigurationService private readonly _textResourceConfigurationService: IResourceConfigurationService,
@IWorkspaceContextService workspaceService: IWorkspaceContextService,
) {
this._cfgFilePath = BreadcrumbsConfig.FilePath.bindTo(_configurationService);
@@ -36,7 +36,7 @@ import { IExtensionGalleryService } from 'vs/platform/extensionManagement/common
import { ITextFileService, SUPPORTED_ENCODINGS } from 'vs/workbench/services/textfile/common/textfiles';
import { ICursorPositionChangedEvent } from 'vs/editor/common/controller/cursorEvents';
import { ConfigurationChangedEvent, IEditorOptions, EditorOption } from 'vs/editor/common/config/editorOptions';
import { ITextResourceConfigurationService } from 'vs/editor/common/services/resourceConfiguration';
import { IResourceConfigurationService } from 'vs/editor/common/services/resourceConfiguration';
import { ConfigurationTarget, IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { deepClone } from 'vs/base/common/objects';
import { ICodeEditor, getCodeEditor } from 'vs/editor/browser/editorBrowser';
@@ -1249,7 +1249,7 @@ export class ChangeEncodingAction extends Action {
actionLabel: string,
@IEditorService private readonly editorService: IEditorService,
@IQuickInputService private readonly quickInputService: IQuickInputService,
@ITextResourceConfigurationService private readonly textResourceConfigurationService: ITextResourceConfigurationService,
@IResourceConfigurationService private readonly textResourceConfigurationService: IResourceConfigurationService,
@IFileService private readonly fileService: IFileService,
@ITextFileService private readonly textFileService: ITextFileService
) {
@@ -16,7 +16,7 @@ import { DiffEditorWidget } from 'vs/editor/browser/widget/diffEditorWidget';
import { TextDiffEditorModel } from 'vs/workbench/common/editor/textDiffEditorModel';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { IStorageService } from 'vs/platform/storage/common/storage';
import { ITextResourceConfigurationService } from 'vs/editor/common/services/resourceConfiguration';
import { IResourceConfigurationService } from 'vs/editor/common/services/resourceConfiguration';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IThemeService } from 'vs/platform/theme/common/themeService';
import { TextFileOperationError, TextFileOperationResult } from 'vs/workbench/services/textfile/common/textfiles';
@@ -48,7 +48,7 @@ export class TextDiffEditor extends BaseTextEditor implements ITextDiffEditor {
@ITelemetryService telemetryService: ITelemetryService,
@IInstantiationService instantiationService: IInstantiationService,
@IStorageService storageService: IStorageService,
@ITextResourceConfigurationService configurationService: ITextResourceConfigurationService,
@IResourceConfigurationService configurationService: IResourceConfigurationService,
@IEditorService editorService: IEditorService,
@IThemeService themeService: IThemeService,
@IEditorGroupsService editorGroupService: IEditorGroupsService,
@@ -16,7 +16,7 @@ import { IStorageService } from 'vs/platform/storage/common/storage';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { IThemeService } from 'vs/platform/theme/common/themeService';
import { ITextResourceConfigurationService } from 'vs/editor/common/services/resourceConfiguration';
import { IResourceConfigurationService } from 'vs/editor/common/services/resourceConfiguration';
import { IEditorOptions } from 'vs/editor/common/config/editorOptions';
import { isCodeEditor, getCodeEditor } from 'vs/editor/browser/editorBrowser';
import { IEditorGroupsService, IEditorGroup } from 'vs/workbench/services/editor/common/editorGroupsService';
@@ -46,7 +46,7 @@ export abstract class BaseTextEditor extends BaseEditor implements ITextEditor {
@ITelemetryService telemetryService: ITelemetryService,
@IInstantiationService private readonly _instantiationService: IInstantiationService,
@IStorageService storageService: IStorageService,
@ITextResourceConfigurationService private readonly _configurationService: ITextResourceConfigurationService,
@IResourceConfigurationService private readonly _configurationService: IResourceConfigurationService,
@IThemeService protected themeService: IThemeService,
@IEditorService protected editorService: IEditorService,
@IEditorGroupsService protected editorGroupService: IEditorGroupsService
@@ -66,7 +66,7 @@ export abstract class BaseTextEditor extends BaseEditor implements ITextEditor {
return this._instantiationService;
}
protected get configurationService(): ITextResourceConfigurationService {
protected get configurationService(): IResourceConfigurationService {
return this._configurationService;
}
@@ -13,7 +13,7 @@ import { UntitledTextEditorInput } from 'vs/workbench/common/editor/untitledText
import { BaseTextEditor } from 'vs/workbench/browser/parts/editor/textEditor';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { IStorageService } from 'vs/platform/storage/common/storage';
import { ITextResourceConfigurationService } from 'vs/editor/common/services/resourceConfiguration';
import { IResourceConfigurationService } from 'vs/editor/common/services/resourceConfiguration';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IThemeService } from 'vs/platform/theme/common/themeService';
import { Event } from 'vs/base/common/event';
@@ -33,7 +33,7 @@ export class AbstractTextResourceEditor extends BaseTextEditor {
@ITelemetryService telemetryService: ITelemetryService,
@IInstantiationService instantiationService: IInstantiationService,
@IStorageService storageService: IStorageService,
@ITextResourceConfigurationService configurationService: ITextResourceConfigurationService,
@IResourceConfigurationService configurationService: IResourceConfigurationService,
@IThemeService themeService: IThemeService,
@IEditorGroupsService editorGroupService: IEditorGroupsService,
@IEditorService editorService: IEditorService
@@ -184,7 +184,7 @@ export class TextResourceEditor extends AbstractTextResourceEditor {
@ITelemetryService telemetryService: ITelemetryService,
@IInstantiationService instantiationService: IInstantiationService,
@IStorageService storageService: IStorageService,
@ITextResourceConfigurationService configurationService: ITextResourceConfigurationService,
@IResourceConfigurationService configurationService: IResourceConfigurationService,
@IThemeService themeService: IThemeService,
@IEditorService editorService: IEditorService,
@IEditorGroupsService editorGroupService: IEditorGroupsService
@@ -1 +0,0 @@
<svg width="11" height="11" viewBox="0 0 11 11" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M6.279 5.5L11 10.221l-.779.779L5.5 6.279.779 11 0 10.221 4.721 5.5 0 .779.779 0 5.5 4.721 10.221 0 11 .779 6.279 5.5z" fill="#fff"/></svg>

Before

Width:  |  Height:  |  Size: 242 B

@@ -1 +0,0 @@
<svg width="11" height="11" viewBox="0 0 11 11" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M6.279 5.5L11 10.221l-.779.779L5.5 6.279.779 11 0 10.221 4.721 5.5 0 .779.779 0 5.5 4.721 10.221 0 11 .779 6.279 5.5z" fill="#000"/></svg>

Before

Width:  |  Height:  |  Size: 242 B

@@ -1 +0,0 @@
<svg width="11" height="11" viewBox="0 0 11 11" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M11 0v11H0V0h11zM9.899 1.101H1.1V9.9H9.9V1.1z" fill="#fff"/></svg>

Before

Width:  |  Height:  |  Size: 170 B

@@ -1 +0,0 @@
<svg width="11" height="11" viewBox="0 0 11 11" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M11 0v11H0V0h11zM9.899 1.101H1.1V9.9H9.9V1.1z" fill="#000"/></svg>

Before

Width:  |  Height:  |  Size: 170 B

@@ -1 +0,0 @@
<svg width="11" height="11" viewBox="0 0 11 11" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M11 4.399V5.5H0V4.399h11z" fill="#fff"/></svg>

Before

Width:  |  Height:  |  Size: 150 B

@@ -1 +0,0 @@
<svg width="11" height="11" viewBox="0 0 11 11" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M11 4.399V5.5H0V4.399h11z" fill="#000"/></svg>

Before

Width:  |  Height:  |  Size: 150 B

@@ -1 +0,0 @@
<svg width="11" height="11" viewBox="0 0 11 11" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M11 8.798H8.798V11H0V2.202h2.202V0H11v8.798zm-3.298-5.5h-6.6v6.6h6.6v-6.6zM9.9 1.1H3.298v1.101h5.5v5.5h1.1v-6.6z" fill="#fff"/></svg>

Before

Width:  |  Height:  |  Size: 237 B

@@ -1 +0,0 @@
<svg width="11" height="11" viewBox="0 0 11 11" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M11 8.798H8.798V11H0V2.202h2.202V0H11v8.798zm-3.298-5.5h-6.6v6.6h6.6v-6.6zM9.9 1.1H3.298v1.101h5.5v5.5h1.1v-6.6z" fill="#000"/></svg>

Before

Width:  |  Height:  |  Size: 237 B

@@ -329,12 +329,9 @@ export class CustomMenubarControl extends MenubarControl {
const menubarActiveWindowFgColor = theme.getColor(TITLE_BAR_ACTIVE_FOREGROUND);
if (menubarActiveWindowFgColor) {
collector.addRule(`
.monaco-workbench .menubar > .menubar-menu-button {
color: ${menubarActiveWindowFgColor};
}
.monaco-workbench .menubar > .menubar-menu-button,
.monaco-workbench .menubar .toolbar-toggle-more {
background-color: ${menubarActiveWindowFgColor}
color: ${menubarActiveWindowFgColor};
}
`);
}
@@ -342,12 +339,9 @@ export class CustomMenubarControl extends MenubarControl {
const activityBarInactiveFgColor = theme.getColor(ACTIVITY_BAR_INACTIVE_FOREGROUND);
if (activityBarInactiveFgColor) {
collector.addRule(`
.monaco-workbench .menubar.compact > .menubar-menu-button {
color: ${activityBarInactiveFgColor};
}
.monaco-workbench .menubar.compact > .menubar-menu-button,
.monaco-workbench .menubar.compact .toolbar-toggle-more {
background-color: ${activityBarInactiveFgColor}
color: ${activityBarInactiveFgColor};
}
`);
@@ -358,14 +352,11 @@ export class CustomMenubarControl extends MenubarControl {
collector.addRule(`
.monaco-workbench .menubar.compact > .menubar-menu-button.open,
.monaco-workbench .menubar.compact > .menubar-menu-button:focus,
.monaco-workbench .menubar.compact:not(:focus-within) > .menubar-menu-button:hover {
color: ${activityBarFgColor};
}
.monaco-workbench .menubar.compact:not(:focus-within) > .menubar-menu-button:hover,
.monaco-workbench .menubar.compact > .menubar-menu-button.open .toolbar-toggle-more,
.monaco-workbench .menubar.compact > .menubar-menu-button:focus .toolbar-toggle-more,
.monaco-workbench .menubar.compact:not(:focus-within) > .menubar-menu-button:hover .toolbar-toggle-more {
background-color: ${activityBarFgColor}
color: ${activityBarFgColor};
}
`);
}
@@ -373,13 +364,10 @@ export class CustomMenubarControl extends MenubarControl {
const menubarInactiveWindowFgColor = theme.getColor(TITLE_BAR_INACTIVE_FOREGROUND);
if (menubarInactiveWindowFgColor) {
collector.addRule(`
.monaco-workbench .menubar.inactive:not(.compact) > .menubar-menu-button {
.monaco-workbench .menubar.inactive:not(.compact) > .menubar-menu-button,
.monaco-workbench .menubar.inactive:not(.compact) > .menubar-menu-button .toolbar-toggle-more {
color: ${menubarInactiveWindowFgColor};
}
.monaco-workbench .menubar.inactive:not(.compact) > .menubar-menu-button .toolbar-toggle-more {
background-color: ${menubarInactiveWindowFgColor}
}
`);
}
@@ -389,14 +377,11 @@ export class CustomMenubarControl extends MenubarControl {
collector.addRule(`
.monaco-workbench .menubar:not(.compact) > .menubar-menu-button.open,
.monaco-workbench .menubar:not(.compact) > .menubar-menu-button:focus,
.monaco-workbench .menubar:not(:focus-within):not(.compact) > .menubar-menu-button:hover {
color: ${menubarSelectedFgColor};
}
.monaco-workbench .menubar:not(:focus-within):not(.compact) > .menubar-menu-button:hover,
.monaco-workbench .menubar:not(.compact) > .menubar-menu-button.open .toolbar-toggle-more,
.monaco-workbench .menubar:not(.compact) > .menubar-menu-button:focus .toolbar-toggle-more,
.monaco-workbench .menubar:not(:focus-within):not(.compact) > .menubar-menu-button:hover .toolbar-toggle-more {
background-color: ${menubarSelectedFgColor}
color: ${menubarSelectedFgColor};
}
`);
}
+14 -2
View File
@@ -492,7 +492,19 @@ export class EditorGroup extends Disposable {
// Add
if (!del && editor) {
this.mru.push(editor); // make it LRU editor
if (this.mru.length === 0) {
// the list of most recent editors is empty
// so this editor can only be the most recent
this.mru.push(editor);
} else {
// we have most recent editors. as such we
// put this newly opened editor right after
// the current most recent one because it cannot
// be the most recently active one unless
// it becomes active. but it is still more
// active then any other editor in the list.
this.mru.splice(1, 0, editor);
}
}
// Remove / Replace
@@ -552,7 +564,7 @@ export class EditorGroup extends Disposable {
// Remove old index
this.mru.splice(mruIndex, 1);
// Set editor to front
// Set editor as most recent one (first)
this.mru.unshift(editor);
}
@@ -12,7 +12,7 @@ import { IModelService } from 'vs/editor/common/services/modelService';
import { Event, Emitter } from 'vs/base/common/event';
import { RunOnceScheduler } from 'vs/base/common/async';
import { IBackupFileService, IResolvedBackup } from 'vs/workbench/services/backup/common/backup';
import { ITextResourceConfigurationService } from 'vs/editor/common/services/resourceConfiguration';
import { IResourceConfigurationService } from 'vs/editor/common/services/resourceConfiguration';
import { ITextBufferFactory } from 'vs/editor/common/model';
import { createTextBufferFactory } from 'vs/editor/common/model/textModel';
import { IResolvedTextEditorModel } from 'vs/editor/common/services/resolverService';
@@ -48,7 +48,7 @@ export class UntitledTextEditorModel extends BaseTextEditorModel implements IEnc
@IModeService modeService: IModeService,
@IModelService modelService: IModelService,
@IBackupFileService private readonly backupFileService: IBackupFileService,
@ITextResourceConfigurationService private readonly configurationService: ITextResourceConfigurationService,
@IResourceConfigurationService private readonly configurationService: IResourceConfigurationService,
@IWorkingCopyService private readonly workingCopyService: IWorkingCopyService,
@ITextFileService private readonly textFileService: ITextFileService
) {
@@ -13,7 +13,7 @@ import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService
import { EditorOption, EditorOptions } from 'vs/editor/common/config/editorOptions';
import { IEditorContribution } from 'vs/editor/common/editorCommon';
import { ITextModel } from 'vs/editor/common/model';
import { ITextResourceConfigurationService } from 'vs/editor/common/services/resourceConfiguration';
import { IResourceConfigurationService } from 'vs/editor/common/services/resourceConfiguration';
import { MenuId, MenuRegistry } from 'vs/platform/actions/common/actions';
import { ContextKeyExpr, IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry';
@@ -54,7 +54,7 @@ function readTransientState(model: ITextModel, codeEditorService: ICodeEditorSer
return codeEditorService.getTransientModelProperty(model, transientWordWrapState);
}
function readWordWrapState(model: ITextModel, configurationService: ITextResourceConfigurationService, codeEditorService: ICodeEditorService): IWordWrapState {
function readWordWrapState(model: ITextModel, configurationService: IResourceConfigurationService, codeEditorService: ICodeEditorService): IWordWrapState {
const editorConfig = configurationService.getValue(model.uri, 'editor') as { wordWrap: 'on' | 'off' | 'wordWrapColumn' | 'bounded'; wordWrapMinified: boolean };
let _configuredWordWrap = editorConfig && (typeof editorConfig.wordWrap === 'string' || typeof editorConfig.wordWrap === 'boolean') ? editorConfig.wordWrap : undefined;
@@ -146,7 +146,7 @@ class ToggleWordWrapAction extends EditorAction {
return;
}
const textResourceConfigurationService = accessor.get(ITextResourceConfigurationService);
const textResourceConfigurationService = accessor.get(IResourceConfigurationService);
const codeEditorService = accessor.get(ICodeEditorService);
const model = editor.getModel();
@@ -171,7 +171,7 @@ class ToggleWordWrapController extends Disposable implements IEditorContribution
constructor(
private readonly editor: ICodeEditor,
@IContextKeyService readonly contextKeyService: IContextKeyService,
@ITextResourceConfigurationService readonly configurationService: ITextResourceConfigurationService,
@IResourceConfigurationService readonly configurationService: IResourceConfigurationService,
@ICodeEditorService readonly codeEditorService: ICodeEditorService
) {
super();
@@ -641,7 +641,7 @@ registerThemingParticipant((theme, collector) => {
.monaco-workbench .codicon-debug-breakpoint-function,
.monaco-workbench .codicon-debug-breakpoint-data,
.monaco-workbench .codicon-debug-breakpoint-unsupported,
.monaco-workbench .codicon-debug-hint:not([class*='codicon-debug-breakpoint']),
.monaco-workbench .codicon-debug-hint:not([class*='codicon-debug-breakpoint']):not([class*='codicon-debug-stackframe']),
.monaco-workbench .codicon-debug-breakpoint.codicon-debug-stackframe-focused::after,
.monaco-workbench .codicon-debug-breakpoint.codicon-debug-stackframe::after {
color: ${debugIconBreakpointColor} !important;
@@ -662,7 +662,7 @@ registerThemingParticipant((theme, collector) => {
if (debugIconBreakpointUnverifiedColor) {
collector.addRule(`
.monaco-workbench .codicon[class*='-unverified'] {
color: ${debugIconBreakpointUnverifiedColor} !important;
color: ${debugIconBreakpointUnverifiedColor};
}
`);
}
@@ -670,7 +670,8 @@ registerThemingParticipant((theme, collector) => {
const debugIconBreakpointCurrentStackframeForegroundColor = theme.getColor(debugIconBreakpointCurrentStackframeForeground);
if (debugIconBreakpointCurrentStackframeForegroundColor) {
collector.addRule(`
.monaco-workbench .codicon-debug-stackframe {
.monaco-workbench .codicon-debug-stackframe,
.monaco-editor .debug-top-stack-frame-column::before {
color: ${debugIconBreakpointCurrentStackframeForegroundColor} !important;
}
`);
@@ -119,7 +119,6 @@ class CallStackEditorContribution implements IEditorContribution {
private static TOP_STACK_FRAME_DECORATION: IModelDecorationOptions = {
isWholeLine: true,
inlineClassName: 'debug-remove-token-colors',
className: 'debug-top-stack-frame-line',
stickiness
};
@@ -130,7 +129,6 @@ class CallStackEditorContribution implements IEditorContribution {
private static FOCUSED_STACK_FRAME_DECORATION: IModelDecorationOptions = {
isWholeLine: true,
inlineClassName: 'debug-remove-token-colors',
className: 'debug-focused-stack-frame-line',
stickiness
};
@@ -84,7 +84,7 @@ Registry.as<ViewletRegistry>(ViewletExtensions.Viewlets).registerViewlet(Viewlet
DebugViewlet,
VIEWLET_ID,
nls.localize('debugAndRun', "Debug and Run"),
'codicon-debug',
'codicon-debug-alt',
13 // {{SQL CARBON EDIT}}
));
@@ -11,7 +11,6 @@ import * as errors from 'vs/base/common/errors';
import severity from 'vs/base/common/severity';
import * as aria from 'vs/base/browser/ui/aria/aria';
import { IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey';
import { IMarkerService } from 'vs/platform/markers/common/markers';
import { ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle';
import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
@@ -22,18 +21,15 @@ import { DebugModel, ExceptionBreakpoint, FunctionBreakpoint, Breakpoint, Expres
import { ViewModel } from 'vs/workbench/contrib/debug/common/debugViewModel';
import * as debugactions from 'vs/workbench/contrib/debug/browser/debugActions';
import { ConfigurationManager } from 'vs/workbench/contrib/debug/browser/debugConfigurationManager';
import Constants from 'vs/workbench/contrib/markers/browser/constants';
import { ITaskService, ITaskSummary } from 'vs/workbench/contrib/tasks/common/taskService';
import { VIEWLET_ID as EXPLORER_VIEWLET_ID } from 'vs/workbench/contrib/files/common/files';
import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet';
import { IPanelService } from 'vs/workbench/services/panel/common/panelService';
import { IWorkbenchLayoutService, Parts } from 'vs/workbench/services/layout/browser/layoutService';
import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { IWorkspaceContextService, WorkbenchState, IWorkspaceFolder, IWorkspace } from 'vs/platform/workspace/common/workspace';
import { IWorkspaceContextService, WorkbenchState, IWorkspaceFolder } from 'vs/platform/workspace/common/workspace';
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
import { parse, getFirstFrame } from 'vs/base/common/console';
import { TaskEvent, TaskEventKind, TaskIdentifier } from 'vs/workbench/contrib/tasks/common/tasks';
import { IDialogService } from 'vs/platform/dialogs/common/dialogs';
import { INotificationService } from 'vs/platform/notification/common/notification';
import { IAction } from 'vs/base/common/actions';
@@ -42,12 +38,13 @@ import { DebugSession } from 'vs/workbench/contrib/debug/browser/debugSession';
import { dispose, IDisposable } from 'vs/base/common/lifecycle';
import { IDebugService, State, IDebugSession, CONTEXT_DEBUG_TYPE, CONTEXT_DEBUG_STATE, CONTEXT_IN_DEBUG_MODE, IThread, IDebugConfiguration, VIEWLET_ID, REPL_ID, IConfig, ILaunch, IViewModel, IConfigurationManager, IDebugModel, IEnablement, IBreakpoint, IBreakpointData, ICompound, IGlobalConfig, IStackFrame, AdapterEndEvent, getStateLabel, IDebugSessionOptions, CONTEXT_DEBUG_UX } from 'vs/workbench/contrib/debug/common/debug';
import { getExtensionHostDebugSession } from 'vs/workbench/contrib/debug/common/debugUtils';
import { isErrorWithActions, createErrorWithActions } from 'vs/base/common/errorsWithActions';
import { isErrorWithActions } from 'vs/base/common/errorsWithActions';
import { RunOnceScheduler } from 'vs/base/common/async';
import { IExtensionHostDebugService } from 'vs/platform/debug/common/extensionHostDebug';
import { isCodeEditor } from 'vs/editor/browser/editorBrowser';
import { CancellationTokenSource } from 'vs/base/common/cancellation';
import { withUndefinedAsNull } from 'vs/base/common/types';
import { TaskRunResult, DebugTaskRunner } from 'vs/workbench/contrib/debug/browser/debugTaskRunner';
import { IActivityService, NumberBadge } from 'vs/workbench/services/activity/common/activity';
const DEBUG_BREAKPOINTS_KEY = 'debug.breakpoint';
const DEBUG_FUNCTION_BREAKPOINTS_KEY = 'debug.functionbreakpoint';
@@ -55,23 +52,6 @@ const DEBUG_DATA_BREAKPOINTS_KEY = 'debug.databreakpoint';
const DEBUG_EXCEPTION_BREAKPOINTS_KEY = 'debug.exceptionbreakpoint';
const DEBUG_WATCH_EXPRESSIONS_KEY = 'debug.watchexpressions';
function once(match: (e: TaskEvent) => boolean, event: Event<TaskEvent>): Event<TaskEvent> {
return (listener, thisArgs = null, disposables?) => {
const result = event(e => {
if (match(e)) {
result.dispose();
return listener.call(thisArgs, e);
}
}, null, disposables);
return result;
};
}
const enum TaskRunResult {
Failure,
Success
}
export class DebugService implements IDebugService {
_serviceBrand: undefined;
@@ -81,6 +61,7 @@ export class DebugService implements IDebugService {
private readonly _onDidEndSession: Emitter<IDebugSession>;
private model: DebugModel;
private viewModel: ViewModel;
private taskRunner: DebugTaskRunner;
private configurationManager: ConfigurationManager;
private toDispose: IDisposable[];
private debugType: IContextKey<string>;
@@ -91,6 +72,7 @@ export class DebugService implements IDebugService {
private initializing = false;
private previousState: State | undefined;
private initCancellationToken: CancellationTokenSource | undefined;
private activity: IDisposable | undefined;
constructor(
@IStorageService private readonly storageService: IStorageService,
@@ -107,11 +89,10 @@ export class DebugService implements IDebugService {
@ILifecycleService private readonly lifecycleService: ILifecycleService,
@IInstantiationService private readonly instantiationService: IInstantiationService,
@IExtensionService private readonly extensionService: IExtensionService,
@IMarkerService private readonly markerService: IMarkerService,
@ITaskService private readonly taskService: ITaskService,
@IFileService private readonly fileService: IFileService,
@IConfigurationService private readonly configurationService: IConfigurationService,
@IExtensionHostDebugService private readonly extensionHostDebugService: IExtensionHostDebugService
@IExtensionHostDebugService private readonly extensionHostDebugService: IExtensionHostDebugService,
@IActivityService private readonly activityService: IActivityService
) {
this.toDispose = [];
@@ -136,6 +117,7 @@ export class DebugService implements IDebugService {
this.toDispose.push(this.model);
this.viewModel = new ViewModel(contextKeyService);
this.taskRunner = this.instantiationService.createInstance(DebugTaskRunner);
this.toDispose.push(this.fileService.onFileChanges(e => this.onFileChanges(e)));
this.lifecycleService.onShutdown(this.dispose, this);
@@ -176,6 +158,16 @@ export class DebugService implements IDebugService {
this.toDispose.push(this.configurationManager.onDidSelectConfiguration(() => {
this.debugUx.set(!!(this.state !== State.Inactive || this.configurationManager.selectedConfiguration.name) ? 'default' : 'simple');
}));
this.toDispose.push(Event.any(this.onDidNewSession, this.onDidEndSession)(() => {
const numberOfSessions = this.model.getSessions().length;
if (numberOfSessions === 0) {
if (this.activity) {
this.activity.dispose();
}
} else {
this.activity = this.activityService.showActivity(VIEWLET_ID, new NumberBadge(numberOfSessions, n => n === 1 ? nls.localize('1activeSession', "1 active session") : nls.localize('nActiveSessions', "{0} active sessions", n)));
}
}));
}
getModel(): IDebugModel {
@@ -299,7 +291,7 @@ export class DebugService implements IDebugService {
"Compound must have \"configurations\" attribute set in order to start multiple configurations."));
}
if (compound.preLaunchTask) {
const taskResult = await this.runTaskAndCheckErrors(launch?.workspace || this.contextService.getWorkspace(), compound.preLaunchTask);
const taskResult = await this.taskRunner.runTaskAndCheckErrors(launch?.workspace || this.contextService.getWorkspace(), compound.preLaunchTask, this.showError);
if (taskResult === TaskRunResult.Failure) {
this.endInitializingState();
return false;
@@ -411,7 +403,7 @@ export class DebugService implements IDebugService {
}
const workspace = launch ? launch.workspace : this.contextService.getWorkspace();
const taskResult = await this.runTaskAndCheckErrors(workspace, resolvedConfig.preLaunchTask);
const taskResult = await this.taskRunner.runTaskAndCheckErrors(workspace, resolvedConfig.preLaunchTask, this.showError);
if (taskResult === TaskRunResult.Success) {
return this.doCreateSession(launch?.workspace, { resolved: resolvedConfig, unresolved: unresolvedConfig }, options);
}
@@ -548,7 +540,7 @@ export class DebugService implements IDebugService {
if (session.configuration.postDebugTask) {
try {
await this.runTask(session.root, session.configuration.postDebugTask);
await this.taskRunner.runTask(session.root, session.configuration.postDebugTask);
} catch (err) {
this.notificationService.error(err);
}
@@ -587,8 +579,8 @@ export class DebugService implements IDebugService {
return Promise.resolve(TaskRunResult.Success);
}
await this.runTask(session.root, session.configuration.postDebugTask);
return this.runTaskAndCheckErrors(session.root, session.configuration.preLaunchTask);
await this.taskRunner.runTask(session.root, session.configuration.postDebugTask);
return this.taskRunner.runTaskAndCheckErrors(session.root, session.configuration.preLaunchTask, this.showError);
};
const extensionDebugSession = getExtensionHostDebugSession(session);
@@ -715,129 +707,6 @@ export class DebugService implements IDebugService {
return undefined;
}
//---- task management
private async runTaskAndCheckErrors(root: IWorkspaceFolder | IWorkspace | undefined, taskId: string | TaskIdentifier | undefined): Promise<TaskRunResult> {
try {
const taskSummary = await this.runTask(root, taskId);
const errorCount = taskId ? this.markerService.getStatistics().errors : 0;
const successExitCode = taskSummary && taskSummary.exitCode === 0;
const failureExitCode = taskSummary && taskSummary.exitCode !== 0;
const onTaskErrors = this.configurationService.getValue<IDebugConfiguration>('debug').onTaskErrors;
if (successExitCode || onTaskErrors === 'debugAnyway' || (errorCount === 0 && !failureExitCode)) {
return TaskRunResult.Success;
}
if (onTaskErrors === 'showErrors') {
this.panelService.openPanel(Constants.MARKERS_PANEL_ID);
return Promise.resolve(TaskRunResult.Failure);
}
const taskLabel = typeof taskId === 'string' ? taskId : taskId ? taskId.name : '';
const message = errorCount > 1
? nls.localize('preLaunchTaskErrors', "Errors exist after running preLaunchTask '{0}'.", taskLabel)
: errorCount === 1
? nls.localize('preLaunchTaskError', "Error exists after running preLaunchTask '{0}'.", taskLabel)
: nls.localize('preLaunchTaskExitCode', "The preLaunchTask '{0}' terminated with exit code {1}.", taskLabel, taskSummary ? taskSummary.exitCode : 0);
const result = await this.dialogService.show(severity.Warning, message, [nls.localize('debugAnyway', "Debug Anyway"), nls.localize('showErrors', "Show Errors"), nls.localize('cancel', "Cancel")], {
checkbox: {
label: nls.localize('remember', "Remember my choice in user settings"),
},
cancelId: 2
});
if (result.choice === 2) {
return Promise.resolve(TaskRunResult.Failure);
}
const debugAnyway = result.choice === 0;
if (result.checkboxChecked) {
this.configurationService.updateValue('debug.onTaskErrors', debugAnyway ? 'debugAnyway' : 'showErrors');
}
if (debugAnyway) {
return TaskRunResult.Success;
}
this.panelService.openPanel(Constants.MARKERS_PANEL_ID);
return Promise.resolve(TaskRunResult.Failure);
} catch (err) {
await this.showError(err.message, [this.taskService.configureAction()]);
return TaskRunResult.Failure;
}
}
private async runTask(root: IWorkspace | IWorkspaceFolder | undefined, taskId: string | TaskIdentifier | undefined): Promise<ITaskSummary | null> {
if (!taskId) {
return Promise.resolve(null);
}
if (!root) {
return Promise.reject(new Error(nls.localize('invalidTaskReference', "Task '{0}' can not be referenced from a launch configuration that is in a different workspace folder.", typeof taskId === 'string' ? taskId : taskId.type)));
}
// run a task before starting a debug session
const task = await this.taskService.getTask(root, taskId);
if (!task) {
const errorMessage = typeof taskId === 'string'
? nls.localize('DebugTaskNotFoundWithTaskId', "Could not find the task '{0}'.", taskId)
: nls.localize('DebugTaskNotFound', "Could not find the specified task.");
return Promise.reject(createErrorWithActions(errorMessage));
}
// If a task is missing the problem matcher the promise will never complete, so we need to have a workaround #35340
let taskStarted = false;
const inactivePromise: Promise<ITaskSummary | null> = new Promise((c, e) => once(e => {
// When a task isBackground it will go inactive when it is safe to launch.
// But when a background task is terminated by the user, it will also fire an inactive event.
// This means that we will not get to see the real exit code from running the task (undefined when terminated by the user).
// Catch the ProcessEnded event here, which occurs before inactive, and capture the exit code to prevent this.
return (e.kind === TaskEventKind.Inactive
|| (e.kind === TaskEventKind.ProcessEnded && e.exitCode === undefined))
&& e.taskId === task._id;
}, this.taskService.onDidStateChange)(e => {
taskStarted = true;
c(e.kind === TaskEventKind.ProcessEnded ? { exitCode: e.exitCode } : null);
}));
const promise: Promise<ITaskSummary | null> = this.taskService.getActiveTasks().then(async (tasks): Promise<ITaskSummary | null> => {
if (tasks.filter(t => t._id === task._id).length) {
// Check that the task isn't busy and if it is, wait for it
const busyTasks = await this.taskService.getBusyTasks();
if (busyTasks.filter(t => t._id === task._id).length) {
taskStarted = true;
return inactivePromise;
}
// task is already running and isn't busy - nothing to do.
return Promise.resolve(null);
}
once(e => ((e.kind === TaskEventKind.Active) || (e.kind === TaskEventKind.DependsOnStarted)) && e.taskId === task._id, this.taskService.onDidStateChange)(() => {
// Task is active, so everything seems to be fine, no need to prompt after 10 seconds
// Use case being a slow running task should not be prompted even though it takes more than 10 seconds
taskStarted = true;
});
const taskPromise = this.taskService.run(task);
if (task.configurationProperties.isBackground) {
return inactivePromise;
}
return taskPromise.then(withUndefinedAsNull);
});
return new Promise((c, e) => {
promise.then(result => {
taskStarted = true;
c(result);
}, error => e(error));
setTimeout(() => {
if (!taskStarted) {
const errorMessage = typeof taskId === 'string'
? nls.localize('taskNotTrackedWithTaskId', "The specified task cannot be tracked.")
: nls.localize('taskNotTracked', "The task '{0}' cannot be tracked.", JSON.stringify(taskId));
e({ severity: severity.Error, message: errorMessage });
}
}, 10000);
});
}
//---- focus management
async focusStackFrame(stackFrame: IStackFrame | undefined, thread?: IThread, session?: IDebugSession, explicit?: boolean): Promise<void> {
@@ -691,6 +691,11 @@ export class DebugSession implements IDebugSession {
}
}
initializeForTest(raw: RawDebugSession): void {
this.raw = raw;
this.registerListeners();
}
//---- private
private registerListeners(): void {
@@ -0,0 +1,169 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as nls from 'vs/nls';
import severity from 'vs/base/common/severity';
import { Event } from 'vs/base/common/event';
import Constants from 'vs/workbench/contrib/markers/browser/constants';
import { ITaskService, ITaskSummary } from 'vs/workbench/contrib/tasks/common/taskService';
import { IPanelService } from 'vs/workbench/services/panel/common/panelService';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { IWorkspaceFolder, IWorkspace } from 'vs/platform/workspace/common/workspace';
import { TaskEvent, TaskEventKind, TaskIdentifier } from 'vs/workbench/contrib/tasks/common/tasks';
import { IDialogService } from 'vs/platform/dialogs/common/dialogs';
import { IAction } from 'vs/base/common/actions';
import { withUndefinedAsNull } from 'vs/base/common/types';
import { IMarkerService } from 'vs/platform/markers/common/markers';
import { IDebugConfiguration } from 'vs/workbench/contrib/debug/common/debug';
import { createErrorWithActions } from 'vs/base/common/errorsWithActions';
function once(match: (e: TaskEvent) => boolean, event: Event<TaskEvent>): Event<TaskEvent> {
return (listener, thisArgs = null, disposables?) => {
const result = event(e => {
if (match(e)) {
result.dispose();
return listener.call(thisArgs, e);
}
}, null, disposables);
return result;
};
}
export const enum TaskRunResult {
Failure,
Success
}
export class DebugTaskRunner {
constructor(
@ITaskService private readonly taskService: ITaskService,
@IMarkerService private readonly markerService: IMarkerService,
@IConfigurationService private readonly configurationService: IConfigurationService,
@IPanelService private readonly panelService: IPanelService,
@IDialogService private readonly dialogService: IDialogService,
) { }
async runTaskAndCheckErrors(root: IWorkspaceFolder | IWorkspace | undefined, taskId: string | TaskIdentifier | undefined, onError: (msg: string, actions: IAction[]) => Promise<void>): Promise<TaskRunResult> {
try {
const taskSummary = await this.runTask(root, taskId);
const errorCount = taskId ? this.markerService.getStatistics().errors : 0;
const successExitCode = taskSummary && taskSummary.exitCode === 0;
const failureExitCode = taskSummary && taskSummary.exitCode !== 0;
const onTaskErrors = this.configurationService.getValue<IDebugConfiguration>('debug').onTaskErrors;
if (successExitCode || onTaskErrors === 'debugAnyway' || (errorCount === 0 && !failureExitCode)) {
return TaskRunResult.Success;
}
if (onTaskErrors === 'showErrors') {
this.panelService.openPanel(Constants.MARKERS_PANEL_ID);
return Promise.resolve(TaskRunResult.Failure);
}
const taskLabel = typeof taskId === 'string' ? taskId : taskId ? taskId.name : '';
const message = errorCount > 1
? nls.localize('preLaunchTaskErrors', "Errors exist after running preLaunchTask '{0}'.", taskLabel)
: errorCount === 1
? nls.localize('preLaunchTaskError', "Error exists after running preLaunchTask '{0}'.", taskLabel)
: nls.localize('preLaunchTaskExitCode', "The preLaunchTask '{0}' terminated with exit code {1}.", taskLabel, taskSummary ? taskSummary.exitCode : 0);
const result = await this.dialogService.show(severity.Warning, message, [nls.localize('debugAnyway', "Debug Anyway"), nls.localize('showErrors', "Show Errors"), nls.localize('cancel', "Cancel")], {
checkbox: {
label: nls.localize('remember', "Remember my choice in user settings"),
},
cancelId: 2
});
if (result.choice === 2) {
return Promise.resolve(TaskRunResult.Failure);
}
const debugAnyway = result.choice === 0;
if (result.checkboxChecked) {
this.configurationService.updateValue('debug.onTaskErrors', debugAnyway ? 'debugAnyway' : 'showErrors');
}
if (debugAnyway) {
return TaskRunResult.Success;
}
this.panelService.openPanel(Constants.MARKERS_PANEL_ID);
return Promise.resolve(TaskRunResult.Failure);
} catch (err) {
await onError(err.message, [this.taskService.configureAction()]);
return TaskRunResult.Failure;
}
}
async runTask(root: IWorkspace | IWorkspaceFolder | undefined, taskId: string | TaskIdentifier | undefined): Promise<ITaskSummary | null> {
if (!taskId) {
return Promise.resolve(null);
}
if (!root) {
return Promise.reject(new Error(nls.localize('invalidTaskReference', "Task '{0}' can not be referenced from a launch configuration that is in a different workspace folder.", typeof taskId === 'string' ? taskId : taskId.type)));
}
// run a task before starting a debug session
const task = await this.taskService.getTask(root, taskId);
if (!task) {
const errorMessage = typeof taskId === 'string'
? nls.localize('DebugTaskNotFoundWithTaskId', "Could not find the task '{0}'.", taskId)
: nls.localize('DebugTaskNotFound', "Could not find the specified task.");
return Promise.reject(createErrorWithActions(errorMessage));
}
// If a task is missing the problem matcher the promise will never complete, so we need to have a workaround #35340
let taskStarted = false;
const inactivePromise: Promise<ITaskSummary | null> = new Promise((c, e) => once(e => {
// When a task isBackground it will go inactive when it is safe to launch.
// But when a background task is terminated by the user, it will also fire an inactive event.
// This means that we will not get to see the real exit code from running the task (undefined when terminated by the user).
// Catch the ProcessEnded event here, which occurs before inactive, and capture the exit code to prevent this.
return (e.kind === TaskEventKind.Inactive
|| (e.kind === TaskEventKind.ProcessEnded && e.exitCode === undefined))
&& e.taskId === task._id;
}, this.taskService.onDidStateChange)(e => {
taskStarted = true;
c(e.kind === TaskEventKind.ProcessEnded ? { exitCode: e.exitCode } : null);
}));
const promise: Promise<ITaskSummary | null> = this.taskService.getActiveTasks().then(async (tasks): Promise<ITaskSummary | null> => {
if (tasks.filter(t => t._id === task._id).length) {
// Check that the task isn't busy and if it is, wait for it
const busyTasks = await this.taskService.getBusyTasks();
if (busyTasks.filter(t => t._id === task._id).length) {
taskStarted = true;
return inactivePromise;
}
// task is already running and isn't busy - nothing to do.
return Promise.resolve(null);
}
once(e => ((e.kind === TaskEventKind.Active) || (e.kind === TaskEventKind.DependsOnStarted)) && e.taskId === task._id, this.taskService.onDidStateChange)(() => {
// Task is active, so everything seems to be fine, no need to prompt after 10 seconds
// Use case being a slow running task should not be prompted even though it takes more than 10 seconds
taskStarted = true;
});
const taskPromise = this.taskService.run(task);
if (task.configurationProperties.isBackground) {
return inactivePromise;
}
return taskPromise.then(withUndefinedAsNull);
});
return new Promise((c, e) => {
promise.then(result => {
taskStarted = true;
c(result);
}, error => e(error));
setTimeout(() => {
if (!taskStarted) {
const errorMessage = typeof taskId === 'string'
? nls.localize('taskNotTrackedWithTaskId', "The specified task cannot be tracked.")
: nls.localize('taskNotTracked', "The task '{0}' cannot be tracked.", JSON.stringify(taskId));
e({ severity: severity.Error, message: errorMessage });
}
}, 10000);
});
}
}
@@ -7,8 +7,8 @@
cursor: pointer;
}
.codicon-debug-hint:not([class*='codicon-debug-breakpoint']) {
opacity: .4 !important;
.codicon-debug-hint:not([class*='codicon-debug-breakpoint']):not([class*='codicon-debug-stackframe']) {
opacity: 0.4 !important;
}
.inline-breakpoint-widget.codicon {
@@ -18,7 +18,7 @@
.codicon-debug-breakpoint.codicon-debug-stackframe-focused::after,
.codicon-debug-breakpoint.codicon-debug-stackframe::after {
content: "\eb8a";
content: '\eb8a';
position: absolute;
}
@@ -28,26 +28,38 @@
.monaco-editor .debug-breakpoint-placeholder::before,
.monaco-editor .debug-top-stack-frame-column::before {
content: " ";
content: ' ';
width: 0.9em;
display: inline-block;
vertical-align: text-bottom;
display: inline-flex;
vertical-align: middle;
margin-right: 2px;
margin-left: 2px;
margin-top: -1px; /* TODO @misolori: figure out a way to not use negative margin for alignment */
}
.monaco-editor .debug-top-stack-frame-column {
display: inline-flex;
vertical-align: middle;
}
.monaco-editor .debug-top-stack-frame-column::before {
content: '\eb8b';
font: normal normal normal 16px/1 codicon;
text-rendering: auto;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
margin-left: 0;
margin-right: 4px;
}
/* Do not show call stack decoration when we plan to show breakpoint and top stack frame in one decoration */
.monaco-editor .debug-breakpoint-placeholder ~ .debug-top-stack-frame-column::before {
width: 0em;
content: "";
content: '';
margin-right: 0px;
margin-left: 0px;
}
.monaco-editor .debug-top-stack-frame-column::before {
height: 1.3em;
}
.monaco-editor .inline-breakpoint-widget {
cursor: pointer;
}
@@ -104,7 +116,7 @@
}
.monaco-workbench .monaco-list-row .expression .name {
color: #9B46B0;
color: #9b46b0;
}
.monaco-workbench .monaco-list-row .expression .name.virtual {
@@ -120,61 +132,61 @@
}
.monaco-workbench .monaco-list-row .expression .error {
color: #E51400;
color: #e51400;
}
.monaco-workbench .monaco-list-row .expression .value.number {
color: #09885A;
color: #09885a;
}
.monaco-workbench .monaco-list-row .expression .value.boolean {
color: #0000FF;
color: #0000ff;
}
.monaco-workbench .monaco-list-row .expression .value.string {
color: #A31515;
color: #a31515;
}
.vs-dark .monaco-workbench > .monaco-list-row .expression .value {
.vs-dark .monaco-workbench > .monaco-list-row .expression .value {
color: rgba(204, 204, 204, 0.6);
}
.vs-dark .monaco-workbench .monaco-list-row .expression .error {
color: #F48771;
}
.vs-dark .monaco-workbench .monaco-list-row .expression .value.number {
color: #B5CEA8;
}
.hc-black .monaco-workbench .monaco-list-row .expression .value.number {
color: #89d185;
}
.hc-black .monaco-workbench .monaco-list-row .expression .value.boolean {
color: #75bdfe;
}
.hc-black .monaco-workbench .monaco-list-row .expression .value.string {
.vs-dark .monaco-workbench .monaco-list-row .expression .error {
color: #f48771;
}
.vs-dark .monaco-workbench .monaco-list-row .expression .value.boolean {
color: #4E94CE;
.vs-dark .monaco-workbench .monaco-list-row .expression .value.number {
color: #b5cea8;
}
.vs-dark .monaco-workbench .monaco-list-row .expression .value.string {
color: #CE9178;
.hc-black .monaco-workbench .monaco-list-row .expression .value.number {
color: #89d185;
}
.hc-black .monaco-workbench .monaco-list-row .expression .value.boolean {
color: #75bdfe;
}
.hc-black .monaco-workbench .monaco-list-row .expression .value.string {
color: #f48771;
}
.vs-dark .monaco-workbench .monaco-list-row .expression .value.boolean {
color: #4e94ce;
}
.vs-dark .monaco-workbench .monaco-list-row .expression .value.string {
color: #ce9178;
}
.hc-black .monaco-workbench .monaco-list-row .expression .error {
color: #F48771;
color: #f48771;
}
/* Dark theme */
.vs-dark .monaco-workbench .monaco-list-row .expression .name {
color: #C586C0;
color: #c586c0;
}
/* High Contrast Theming */
@@ -182,7 +194,3 @@
.hc-black .monaco-workbench .monaco-list-row .expression .name {
color: inherit;
}
.hc-black .monaco-editor .debug-remove-token-colors {
color:black;
}
@@ -80,7 +80,6 @@ export class RawDebugSession implements IDisposable {
public readonly customTelemetryService: ITelemetryService | undefined,
private readonly extensionHostDebugService: IExtensionHostDebugService,
private readonly openerService: IOpenerService
) {
this.debugAdapter = debugAdapter;
this._capabilities = Object.create(null);
@@ -598,13 +597,13 @@ export class RawDebugSession implements IDisposable {
}
private send<R extends DebugProtocol.Response>(command: string, args: any, token?: CancellationToken, timeout?: number): Promise<R> {
return new Promise<R>((completeDispatch, errorDispatch) => {
return new Promise<DebugProtocol.Response>((completeDispatch, errorDispatch) => {
if (!this.debugAdapter) {
errorDispatch(new Error('no debug adapter found'));
return;
}
let cancelationListener: IDisposable;
const requestId = this.debugAdapter.sendRequest(command, args, (response: R) => {
const requestId = this.debugAdapter.sendRequest(command, args, (response: DebugProtocol.Response) => {
if (cancelationListener) {
cancelationListener.dispose();
}
@@ -25,7 +25,6 @@ import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent';
import { KeyCode } from 'vs/base/common/keyCodes';
const $ = dom.$;
export class StartView extends ViewPane {
static ID = 'workbench.debug.startView';
@@ -5,7 +5,7 @@
import { Emitter, Event } from 'vs/base/common/event';
import { IDebugAdapter } from 'vs/workbench/contrib/debug/common/debug';
import { timeout } from 'vs/base/common/async';
import { timeout, Queue } from 'vs/base/common/async';
/**
* Abstract implementation of the low level API for a debug adapter.
@@ -18,6 +18,7 @@ export abstract class AbstractDebugAdapter implements IDebugAdapter {
private requestCallback: ((request: DebugProtocol.Request) => void) | undefined;
private eventCallback: ((request: DebugProtocol.Event) => void) | undefined;
private messageCallback: ((message: DebugProtocol.ProtocolMessage) => void) | undefined;
private readonly queue = new Queue();
protected readonly _onError = new Emitter<Error>();
protected readonly _onExit = new Emitter<number | null>();
@@ -108,26 +109,33 @@ export abstract class AbstractDebugAdapter implements IDebugAdapter {
this.messageCallback(message);
}
else {
switch (message.type) {
case 'event':
if (this.eventCallback) {
this.eventCallback(<DebugProtocol.Event>message);
}
break;
case 'request':
if (this.requestCallback) {
this.requestCallback(<DebugProtocol.Request>message);
}
break;
case 'response':
const response = <DebugProtocol.Response>message;
const clb = this.pendingRequests.get(response.request_seq);
if (clb) {
this.pendingRequests.delete(response.request_seq);
clb(response);
}
break;
}
this.queue.queue(() => {
switch (message.type) {
case 'event':
if (this.eventCallback) {
this.eventCallback(<DebugProtocol.Event>message);
}
break;
case 'request':
if (this.requestCallback) {
this.requestCallback(<DebugProtocol.Request>message);
}
break;
case 'response':
const response = <DebugProtocol.Response>message;
const clb = this.pendingRequests.get(response.request_seq);
if (clb) {
this.pendingRequests.delete(response.request_seq);
clb(response);
}
break;
}
// Artificially queueing protocol messages guarantees that any microtasks for
// previous message finish before next message is processed. This is essential
// to guarantee ordering when using promises anywhere along the call path.
return timeout(0);
});
}
}
@@ -164,6 +172,6 @@ export abstract class AbstractDebugAdapter implements IDebugAdapter {
}
dispose(): void {
// noop
this.queue.dispose();
}
}
@@ -548,7 +548,7 @@ export interface IDebugAdapterServer {
}
export interface IDebugAdapterInlineImpl extends IDisposable {
readonly onSendMessage: Event<DebugProtocol.Message>;
readonly onDidSendMessage: Event<DebugProtocol.Message>;
handleMessage(message: DebugProtocol.Message): void;
}
@@ -8,12 +8,14 @@ import { URI as uri } from 'vs/base/common/uri';
import severity from 'vs/base/common/severity';
import { DebugModel, Expression, StackFrame, Thread } from 'vs/workbench/contrib/debug/common/debugModel';
import * as sinon from 'sinon';
import { MockRawSession } from 'vs/workbench/contrib/debug/test/common/mockDebug';
import { MockRawSession, MockDebugAdapter } from 'vs/workbench/contrib/debug/test/common/mockDebug';
import { Source } from 'vs/workbench/contrib/debug/common/debugSource';
import { DebugSession } from 'vs/workbench/contrib/debug/browser/debugSession';
import { SimpleReplElement, RawObjectReplElement, ReplEvaluationInput, ReplModel } from 'vs/workbench/contrib/debug/common/replModel';
import { SimpleReplElement, RawObjectReplElement, ReplEvaluationInput, ReplModel, ReplEvaluationResult } from 'vs/workbench/contrib/debug/common/replModel';
import { IBreakpointUpdateData, IDebugSessionOptions } from 'vs/workbench/contrib/debug/common/debug';
import { NullOpenerService } from 'vs/platform/opener/common/opener';
import { RawDebugSession } from 'vs/workbench/contrib/debug/browser/rawDebugSession';
import { timeout } from 'vs/base/common/async';
function createMockSession(model: DebugModel, name = 'mockSession', options?: IDebugSessionOptions): DebugSession {
return new DebugSession({ resolved: { name, type: 'node', request: 'launch' }, unresolved: undefined }, undefined!, model, options, undefined!, undefined!, undefined!, undefined!, undefined!, undefined!, undefined!, undefined!, NullOpenerService);
@@ -548,4 +550,26 @@ suite('Debug - Model', () => {
assert.equal(grandChild.getReplElements().length, 2);
assert.equal(child3.getReplElements().length, 1);
});
test('repl ordering', async () => {
const session = createMockSession(model);
model.addSession(session);
const adapter = new MockDebugAdapter();
const raw = new RawDebugSession(adapter, undefined!, undefined!, undefined!, undefined!, undefined!);
session.initializeForTest(raw);
await session.addReplExpression(undefined, 'before.1');
assert.equal(session.getReplElements().length, 3);
assert.equal((<ReplEvaluationInput>session.getReplElements()[0]).value, 'before.1');
assert.equal((<SimpleReplElement>session.getReplElements()[1]).value, 'before.1');
assert.equal((<ReplEvaluationResult>session.getReplElements()[2]).value, '=before.1');
await session.addReplExpression(undefined, 'after.2');
await timeout(0);
assert.equal(session.getReplElements().length, 6);
assert.equal((<ReplEvaluationInput>session.getReplElements()[3]).value, 'after.2');
assert.equal((<ReplEvaluationResult>session.getReplElements()[4]).value, '=after.2');
assert.equal((<SimpleReplElement>session.getReplElements()[5]).value, 'after.2');
});
});
@@ -11,6 +11,7 @@ import { ILaunch, IDebugService, State, IDebugSession, IConfigurationManager, IS
import { Source } from 'vs/workbench/contrib/debug/common/debugSource';
import { CompletionItem } from 'vs/editor/common/modes';
import Severity from 'vs/base/common/severity';
import { AbstractDebugAdapter } from 'vs/workbench/contrib/debug/common/abstractDebugAdapter';
export class MockDebugService implements IDebugService {
@@ -464,3 +465,67 @@ export class MockRawSession {
public readonly onDidStop: Event<DebugProtocol.StoppedEvent> = null!;
}
export class MockDebugAdapter extends AbstractDebugAdapter {
private seq = 0;
startSession(): Promise<void> {
return Promise.resolve();
}
stopSession(): Promise<void> {
return Promise.resolve();
}
sendMessage(message: DebugProtocol.ProtocolMessage): void {
setTimeout(() => {
if (message.type === 'request') {
const request = message as DebugProtocol.Request;
switch (request.command) {
case 'evaluate':
this.evaluate(request, request.arguments);
return;
}
this.sendResponseBody(request, {});
return;
}
}, 0);
}
sendResponseBody(request: DebugProtocol.Request, body: any) {
const response: DebugProtocol.Response = {
seq: ++this.seq,
type: 'response',
request_seq: request.seq,
command: request.command,
success: true,
body
};
this.acceptMessage(response);
}
sendEventBody(event: string, body: any) {
const response: DebugProtocol.Event = {
seq: ++this.seq,
type: 'event',
event,
body
};
this.acceptMessage(response);
}
evaluate(request: DebugProtocol.Request, args: DebugProtocol.EvaluateArguments) {
if (args.expression.indexOf('before.') === 0) {
this.sendEventBody('output', { output: args.expression });
}
this.sendResponseBody(request, {
result: '=' + args.expression,
variablesReference: 0
});
if (args.expression.indexOf('after.') === 0) {
this.sendEventBody('output', { output: args.expression });
}
}
}
@@ -20,7 +20,7 @@ import { FileOperationError, FileOperationResult, FileChangesEvent, IFileService
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
import { IStorageService } from 'vs/platform/storage/common/storage';
import { ITextResourceConfigurationService } from 'vs/editor/common/services/resourceConfiguration';
import { IResourceConfigurationService } from 'vs/editor/common/services/resourceConfiguration';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IThemeService } from 'vs/platform/theme/common/themeService';
import { ScrollType } from 'vs/editor/common/editorCommon';
@@ -49,7 +49,7 @@ export class TextFileEditor extends BaseTextEditor {
@IInstantiationService instantiationService: IInstantiationService,
@IWorkspaceContextService private readonly contextService: IWorkspaceContextService,
@IStorageService storageService: IStorageService,
@ITextResourceConfigurationService configurationService: ITextResourceConfigurationService,
@IResourceConfigurationService configurationService: IResourceConfigurationService,
@IEditorService editorService: IEditorService,
@IThemeService themeService: IThemeService,
@IEditorGroupsService editorGroupService: IEditorGroupsService,

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