Vscode merge (#4582)

* Merge from vscode 37cb23d3dd4f9433d56d4ba5ea3203580719a0bd

* fix issues with merges

* bump node version in azpipe

* replace license headers

* remove duplicate launch task

* fix build errors

* fix build errors

* fix tslint issues

* working through package and linux build issues

* more work

* wip

* fix packaged builds

* working through linux build errors

* wip

* wip

* wip

* fix mac and linux file limits

* iterate linux pipeline

* disable editor typing

* revert series to parallel

* remove optimize vscode from linux

* fix linting issues

* revert testing change

* add work round for new node

* readd packaging for extensions

* fix issue with angular not resolving decorator dependencies
This commit is contained in:
Anthony Dresser
2019-03-19 17:44:35 -07:00
committed by GitHub
parent 833d197412
commit 87765e8673
1879 changed files with 54505 additions and 38058 deletions

View File

@@ -0,0 +1,337 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import 'vs/css!./media/keybindings';
import * as nls from 'vs/nls';
import { OS } from 'vs/base/common/platform';
import { Disposable, dispose, toDisposable, IDisposable } from 'vs/base/common/lifecycle';
import { Event, Emitter } from 'vs/base/common/event';
import { KeybindingLabel } from 'vs/base/browser/ui/keybindingLabel/keybindingLabel';
import { Widget } from 'vs/base/browser/ui/widget';
import { ResolvedKeybinding, KeyCode } from 'vs/base/common/keyCodes';
import * as dom from 'vs/base/browser/dom';
import { IKeyboardEvent, StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent';
import { FastDomNode, createFastDomNode } from 'vs/base/browser/fastDomNode';
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
import { IContextViewService } from 'vs/platform/contextview/browser/contextView';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { ICodeEditor, IOverlayWidget, IOverlayWidgetPosition } from 'vs/editor/browser/editorBrowser';
import { attachInputBoxStyler, attachStylerCallback } from 'vs/platform/theme/common/styler';
import { IThemeService } from 'vs/platform/theme/common/themeService';
import { editorWidgetBackground, widgetShadow } from 'vs/platform/theme/common/colorRegistry';
import { ScrollType } from 'vs/editor/common/editorCommon';
import { SearchWidget, SearchOptions } from 'vs/workbench/contrib/preferences/browser/preferencesWidgets';
export interface KeybindingsSearchOptions extends SearchOptions {
recordEnter?: boolean;
quoteRecordedKeys?: boolean;
}
export class KeybindingsSearchWidget extends SearchWidget {
private _firstPart: ResolvedKeybinding | null;
private _chordPart: ResolvedKeybinding | null;
private _inputValue: string;
private recordDisposables: IDisposable[] = [];
private _onKeybinding = this._register(new Emitter<[ResolvedKeybinding | null, ResolvedKeybinding | null]>());
readonly onKeybinding: Event<[ResolvedKeybinding | null, ResolvedKeybinding | null]> = this._onKeybinding.event;
private _onEnter = this._register(new Emitter<void>());
readonly onEnter: Event<void> = this._onEnter.event;
private _onEscape = this._register(new Emitter<void>());
readonly onEscape: Event<void> = this._onEscape.event;
private _onBlur = this._register(new Emitter<void>());
readonly onBlur: Event<void> = this._onBlur.event;
constructor(parent: HTMLElement, options: SearchOptions,
@IContextViewService contextViewService: IContextViewService,
@IKeybindingService private readonly keybindingService: IKeybindingService,
@IInstantiationService instantiationService: IInstantiationService,
@IThemeService themeService: IThemeService
) {
super(parent, options, contextViewService, instantiationService, themeService);
this._register(attachInputBoxStyler(this.inputBox, themeService));
this._register(toDisposable(() => this.stopRecordingKeys()));
this._reset();
}
clear(): void {
this._reset();
super.clear();
}
startRecordingKeys(): void {
this.recordDisposables.push(dom.addDisposableListener(this.inputBox.inputElement, dom.EventType.KEY_DOWN, (e: KeyboardEvent) => this._onKeyDown(new StandardKeyboardEvent(e))));
this.recordDisposables.push(dom.addDisposableListener(this.inputBox.inputElement, dom.EventType.BLUR, () => this._onBlur.fire()));
this.recordDisposables.push(dom.addDisposableListener(this.inputBox.inputElement, dom.EventType.INPUT, () => {
// Prevent other characters from showing up
this.setInputValue(this._inputValue);
}));
}
stopRecordingKeys(): void {
this._reset();
dispose(this.recordDisposables);
}
setInputValue(value: string): void {
this._inputValue = value;
this.inputBox.value = this._inputValue;
}
private _reset() {
this._firstPart = null;
this._chordPart = null;
}
private _onKeyDown(keyboardEvent: IKeyboardEvent): void {
keyboardEvent.preventDefault();
keyboardEvent.stopPropagation();
const options = this.options as KeybindingsSearchOptions;
if (!options.recordEnter && keyboardEvent.equals(KeyCode.Enter)) {
this._onEnter.fire();
return;
}
if (keyboardEvent.equals(KeyCode.Escape)) {
this._onEscape.fire();
return;
}
this.printKeybinding(keyboardEvent);
}
private printKeybinding(keyboardEvent: IKeyboardEvent): void {
const keybinding = this.keybindingService.resolveKeyboardEvent(keyboardEvent);
const info = `code: ${keyboardEvent.browserEvent.code}, keyCode: ${keyboardEvent.browserEvent.keyCode}, key: ${keyboardEvent.browserEvent.key} => UI: ${keybinding.getAriaLabel()}, user settings: ${keybinding.getUserSettingsLabel()}, dispatch: ${keybinding.getDispatchParts()[0]}`;
const options = this.options as KeybindingsSearchOptions;
const hasFirstPart = (this._firstPart && this._firstPart.getDispatchParts()[0] !== null);
const hasChordPart = (this._chordPart && this._chordPart.getDispatchParts()[0] !== null);
if (hasFirstPart && hasChordPart) {
// Reset
this._firstPart = keybinding;
this._chordPart = null;
} else if (!hasFirstPart) {
this._firstPart = keybinding;
} else {
this._chordPart = keybinding;
}
let value = '';
if (this._firstPart) {
value = (this._firstPart.getUserSettingsLabel() || '');
}
if (this._chordPart) {
value = value + ' ' + this._chordPart.getUserSettingsLabel();
}
this.setInputValue(options.quoteRecordedKeys ? `"${value}"` : value);
this.inputBox.inputElement.title = info;
this._onKeybinding.fire([this._firstPart, this._chordPart]);
}
}
export class DefineKeybindingWidget extends Widget {
private static readonly WIDTH = 400;
private static readonly HEIGHT = 110;
private _domNode: FastDomNode<HTMLElement>;
private _keybindingInputWidget: KeybindingsSearchWidget;
private _outputNode: HTMLElement;
private _showExistingKeybindingsNode: HTMLElement;
private _firstPart: ResolvedKeybinding | null = null;
private _chordPart: ResolvedKeybinding | null = null;
private _isVisible: boolean = false;
private _onHide = this._register(new Emitter<void>());
private _onDidChange = this._register(new Emitter<string>());
onDidChange: Event<string> = this._onDidChange.event;
private _onShowExistingKeybindings = this._register(new Emitter<string | null>());
readonly onShowExistingKeybidings: Event<string | null> = this._onShowExistingKeybindings.event;
constructor(
parent: HTMLElement,
@IInstantiationService private readonly instantiationService: IInstantiationService,
@IThemeService private readonly themeService: IThemeService
) {
super();
this.create();
if (parent) {
dom.append(parent, this._domNode.domNode);
}
}
get domNode(): HTMLElement {
return this._domNode.domNode;
}
define(): Promise<string | null> {
this._keybindingInputWidget.clear();
return new Promise<string | null>((c) => {
if (!this._isVisible) {
this._isVisible = true;
this._domNode.setDisplay('block');
this._firstPart = null;
this._chordPart = null;
this._keybindingInputWidget.setInputValue('');
dom.clearNode(this._outputNode);
dom.clearNode(this._showExistingKeybindingsNode);
this._keybindingInputWidget.focus();
}
const disposable = this._onHide.event(() => {
c(this.getUserSettingsLabel());
disposable.dispose();
});
});
}
layout(layout: dom.Dimension): void {
const top = Math.round((layout.height - DefineKeybindingWidget.HEIGHT) / 2);
this._domNode.setTop(top);
const left = Math.round((layout.width - DefineKeybindingWidget.WIDTH) / 2);
this._domNode.setLeft(left);
}
printExisting(numberOfExisting: number): void {
if (numberOfExisting > 0) {
const existingElement = dom.$('span.existingText');
const text = numberOfExisting === 1 ? nls.localize('defineKeybinding.oneExists', "1 existing command has this keybinding", numberOfExisting) : nls.localize('defineKeybinding.existing', "{0} existing commands have this keybinding", numberOfExisting);
dom.append(existingElement, document.createTextNode(text));
this._showExistingKeybindingsNode.appendChild(existingElement);
existingElement.onmousedown = (e) => { e.preventDefault(); };
existingElement.onmouseup = (e) => { e.preventDefault(); };
existingElement.onclick = () => { this._onShowExistingKeybindings.fire(this.getUserSettingsLabel()); };
}
}
private create(): void {
this._domNode = createFastDomNode(document.createElement('div'));
this._domNode.setDisplay('none');
this._domNode.setClassName('defineKeybindingWidget');
this._domNode.setWidth(DefineKeybindingWidget.WIDTH);
this._domNode.setHeight(DefineKeybindingWidget.HEIGHT);
const message = nls.localize('defineKeybinding.initial', "Press desired key combination and then press ENTER.");
dom.append(this._domNode.domNode, dom.$('.message', undefined, message));
this._register(attachStylerCallback(this.themeService, { editorWidgetBackground, widgetShadow }, colors => {
if (colors.editorWidgetBackground) {
this._domNode.domNode.style.backgroundColor = colors.editorWidgetBackground.toString();
} else {
this._domNode.domNode.style.backgroundColor = null;
}
if (colors.widgetShadow) {
this._domNode.domNode.style.boxShadow = `0 2px 8px ${colors.widgetShadow}`;
} else {
this._domNode.domNode.style.boxShadow = null;
}
}));
this._keybindingInputWidget = this._register(this.instantiationService.createInstance(KeybindingsSearchWidget, this._domNode.domNode, { ariaLabel: message }));
this._keybindingInputWidget.startRecordingKeys();
this._register(this._keybindingInputWidget.onKeybinding(keybinding => this.onKeybinding(keybinding)));
this._register(this._keybindingInputWidget.onEnter(() => this.hide()));
this._register(this._keybindingInputWidget.onEscape(() => this.onCancel()));
this._register(this._keybindingInputWidget.onBlur(() => this.onCancel()));
this._outputNode = dom.append(this._domNode.domNode, dom.$('.output'));
this._showExistingKeybindingsNode = dom.append(this._domNode.domNode, dom.$('.existing'));
}
private onKeybinding(keybinding: [ResolvedKeybinding | null, ResolvedKeybinding | null]): void {
const [firstPart, chordPart] = keybinding;
this._firstPart = firstPart;
this._chordPart = chordPart;
dom.clearNode(this._outputNode);
dom.clearNode(this._showExistingKeybindingsNode);
new KeybindingLabel(this._outputNode, OS).set(this._firstPart);
if (this._chordPart) {
this._outputNode.appendChild(document.createTextNode(nls.localize('defineKeybinding.chordsTo', "chord to")));
new KeybindingLabel(this._outputNode, OS).set(this._chordPart);
}
const label = this.getUserSettingsLabel();
if (label) {
this._onDidChange.fire(label);
}
}
private getUserSettingsLabel(): string | null {
let label: string | null = null;
if (this._firstPart) {
label = this._firstPart.getUserSettingsLabel();
if (this._chordPart) {
label = label + ' ' + this._chordPart.getUserSettingsLabel();
}
}
return label;
}
private onCancel(): void {
this._firstPart = null;
this._chordPart = null;
this.hide();
}
private hide(): void {
this._domNode.setDisplay('none');
this._isVisible = false;
this._onHide.fire();
}
}
export class DefineKeybindingOverlayWidget extends Disposable implements IOverlayWidget {
private static readonly ID = 'editor.contrib.defineKeybindingWidget';
private readonly _widget: DefineKeybindingWidget;
constructor(private _editor: ICodeEditor,
@IInstantiationService instantiationService: IInstantiationService
) {
super();
this._widget = instantiationService.createInstance(DefineKeybindingWidget, null);
this._editor.addOverlayWidget(this);
}
getId(): string {
return DefineKeybindingOverlayWidget.ID;
}
getDomNode(): HTMLElement {
return this._widget.domNode;
}
getPosition(): IOverlayWidgetPosition {
return {
preference: null
};
}
dispose(): void {
this._editor.removeOverlayWidget(this);
super.dispose();
}
start(): Promise<string | null> {
if (this._editor.hasModel()) {
this._editor.revealPositionInCenterIfOutsideViewport(this._editor.getPosition(), ScrollType.Smooth);
}
const layoutInfo = this._editor.getLayoutInfo();
this._widget.layout(new dom.Dimension(layoutInfo.width, layoutInfo.height));
return this._widget.define();
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,402 @@
/*---------------------------------------------------------------------------------------------
* 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 { RunOnceScheduler } from 'vs/base/common/async';
import { MarkdownString } from 'vs/base/common/htmlContent';
import { KeyCode, KeyMod, KeyChord, SimpleKeybinding } from 'vs/base/common/keyCodes';
import { Disposable } from 'vs/base/common/lifecycle';
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
import { Range } from 'vs/editor/common/core/range';
import * as editorCommon from 'vs/editor/common/editorCommon';
import { registerEditorContribution, ServicesAccessor, registerEditorCommand, EditorCommand } from 'vs/editor/browser/editorExtensions';
import { ICodeEditor, IActiveCodeEditor } from 'vs/editor/browser/editorBrowser';
import { SnippetController2 } from 'vs/editor/contrib/snippet/snippetController2';
import { SmartSnippetInserter } from 'vs/workbench/contrib/preferences/common/smartSnippetInserter';
import { DefineKeybindingOverlayWidget } from 'vs/workbench/contrib/preferences/browser/keybindingWidgets';
import { FloatingClickWidget } from 'vs/workbench/browser/parts/editor/editorWidgets';
import { parseTree, Node } from 'vs/base/common/json';
import { ScanCodeBinding } from 'vs/base/common/scanCode';
import { EditorContextKeys } from 'vs/editor/common/editorContextKeys';
import { WindowsNativeResolvedKeybinding } from 'vs/workbench/services/keybinding/common/windowsKeyboardMapper';
import { themeColorFromId, ThemeColor } from 'vs/platform/theme/common/themeService';
import { overviewRulerInfo, overviewRulerError } from 'vs/editor/common/view/editorColorRegistry';
import { IModelDeltaDecoration, ITextModel, TrackedRangeStickiness, OverviewRulerLane } from 'vs/editor/common/model';
import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry';
import { KeybindingParser } from 'vs/base/common/keybindingParser';
const NLS_LAUNCH_MESSAGE = nls.localize('defineKeybinding.start', "Define Keybinding");
const NLS_KB_LAYOUT_ERROR_MESSAGE = nls.localize('defineKeybinding.kbLayoutErrorMessage', "You won't be able to produce this key combination under your current keyboard layout.");
const INTERESTING_FILE = /keybindings\.json$/;
export class DefineKeybindingController extends Disposable implements editorCommon.IEditorContribution {
private static readonly ID = 'editor.contrib.defineKeybinding';
static get(editor: ICodeEditor): DefineKeybindingController {
return editor.getContribution<DefineKeybindingController>(DefineKeybindingController.ID);
}
private _keybindingWidgetRenderer?: KeybindingWidgetRenderer;
private _keybindingDecorationRenderer?: KeybindingEditorDecorationsRenderer;
constructor(
private _editor: ICodeEditor,
@IInstantiationService private readonly _instantiationService: IInstantiationService
) {
super();
this._register(this._editor.onDidChangeModel(e => this._update()));
this._update();
}
getId(): string {
return DefineKeybindingController.ID;
}
get keybindingWidgetRenderer(): KeybindingWidgetRenderer | undefined {
return this._keybindingWidgetRenderer;
}
dispose(): void {
this._disposeKeybindingWidgetRenderer();
this._disposeKeybindingDecorationRenderer();
super.dispose();
}
private _update(): void {
if (!isInterestingEditorModel(this._editor)) {
this._disposeKeybindingWidgetRenderer();
this._disposeKeybindingDecorationRenderer();
return;
}
// Decorations are shown for the default keybindings.json **and** for the user keybindings.json
this._createKeybindingDecorationRenderer();
// The button to define keybindings is shown only for the user keybindings.json
if (!this._editor.getConfiguration().readOnly) {
this._createKeybindingWidgetRenderer();
} else {
this._disposeKeybindingWidgetRenderer();
}
}
private _createKeybindingWidgetRenderer(): void {
if (!this._keybindingWidgetRenderer) {
this._keybindingWidgetRenderer = this._instantiationService.createInstance(KeybindingWidgetRenderer, this._editor);
}
}
private _disposeKeybindingWidgetRenderer(): void {
if (this._keybindingWidgetRenderer) {
this._keybindingWidgetRenderer.dispose();
this._keybindingWidgetRenderer = undefined;
}
}
private _createKeybindingDecorationRenderer(): void {
if (!this._keybindingDecorationRenderer) {
this._keybindingDecorationRenderer = this._instantiationService.createInstance(KeybindingEditorDecorationsRenderer, this._editor);
}
}
private _disposeKeybindingDecorationRenderer(): void {
if (this._keybindingDecorationRenderer) {
this._keybindingDecorationRenderer.dispose();
this._keybindingDecorationRenderer = undefined;
}
}
}
export class KeybindingWidgetRenderer extends Disposable {
private _launchWidget: FloatingClickWidget;
private _defineWidget: DefineKeybindingOverlayWidget;
constructor(
private _editor: ICodeEditor,
@IInstantiationService private readonly _instantiationService: IInstantiationService
) {
super();
this._launchWidget = this._register(this._instantiationService.createInstance(FloatingClickWidget, this._editor, NLS_LAUNCH_MESSAGE, DefineKeybindingCommand.ID));
this._register(this._launchWidget.onClick(() => this.showDefineKeybindingWidget()));
this._defineWidget = this._register(this._instantiationService.createInstance(DefineKeybindingOverlayWidget, this._editor));
this._launchWidget.render();
}
showDefineKeybindingWidget(): void {
this._defineWidget.start().then(keybinding => this._onAccepted(keybinding));
}
private _onAccepted(keybinding: string | null): void {
this._editor.focus();
if (keybinding && this._editor.hasModel()) {
const regexp = new RegExp(/\\/g);
const backslash = regexp.test(keybinding);
if (backslash) {
keybinding = keybinding.slice(0, -1) + '\\\\';
}
let snippetText = [
'{',
'\t"key": ' + JSON.stringify(keybinding) + ',',
'\t"command": "${1:commandId}",',
'\t"when": "${2:editorTextFocus}"',
'}$0'
].join('\n');
const smartInsertInfo = SmartSnippetInserter.insertSnippet(this._editor.getModel(), this._editor.getPosition());
snippetText = smartInsertInfo.prepend + snippetText + smartInsertInfo.append;
this._editor.setPosition(smartInsertInfo.position);
SnippetController2.get(this._editor).insert(snippetText, 0, 0);
}
}
}
export class KeybindingEditorDecorationsRenderer extends Disposable {
private _updateDecorations: RunOnceScheduler;
private _dec: string[] = [];
constructor(
private _editor: IActiveCodeEditor,
@IKeybindingService private readonly _keybindingService: IKeybindingService,
) {
super();
this._updateDecorations = this._register(new RunOnceScheduler(() => this._updateDecorationsNow(), 500));
const model = this._editor.getModel();
this._register(model.onDidChangeContent(() => this._updateDecorations.schedule()));
this._register(this._keybindingService.onDidUpdateKeybindings((e) => this._updateDecorations.schedule()));
this._register({
dispose: () => {
this._dec = this._editor.deltaDecorations(this._dec, []);
this._updateDecorations.cancel();
}
});
this._updateDecorations.schedule();
}
private _updateDecorationsNow(): void {
const model = this._editor.getModel();
const newDecorations: IModelDeltaDecoration[] = [];
const root = parseTree(model.getValue());
if (root && Array.isArray(root.children)) {
for (let i = 0, len = root.children.length; i < len; i++) {
const entry = root.children[i];
const dec = this._getDecorationForEntry(model, entry);
if (dec !== null) {
newDecorations.push(dec);
}
}
}
this._dec = this._editor.deltaDecorations(this._dec, newDecorations);
}
private _getDecorationForEntry(model: ITextModel, entry: Node): IModelDeltaDecoration | null {
if (!Array.isArray(entry.children)) {
return null;
}
for (let i = 0, len = entry.children.length; i < len; i++) {
const prop = entry.children[i];
if (prop.type !== 'property') {
continue;
}
if (!Array.isArray(prop.children) || prop.children.length !== 2) {
continue;
}
const key = prop.children[0];
if (key.value !== 'key') {
continue;
}
const value = prop.children[1];
if (value.type !== 'string') {
continue;
}
const resolvedKeybindings = this._keybindingService.resolveUserBinding(value.value);
if (resolvedKeybindings.length === 0) {
return this._createDecoration(true, null, null, model, value);
}
const resolvedKeybinding = resolvedKeybindings[0];
let usLabel: string | null = null;
if (resolvedKeybinding instanceof WindowsNativeResolvedKeybinding) {
usLabel = resolvedKeybinding.getUSLabel();
}
if (!resolvedKeybinding.isWYSIWYG()) {
const uiLabel = resolvedKeybinding.getLabel();
if (typeof uiLabel === 'string' && value.value.toLowerCase() === uiLabel.toLowerCase()) {
// coincidentally, this is actually WYSIWYG
return null;
}
return this._createDecoration(false, resolvedKeybinding.getLabel(), usLabel, model, value);
}
if (/abnt_|oem_/.test(value.value)) {
return this._createDecoration(false, resolvedKeybinding.getLabel(), usLabel, model, value);
}
const expectedUserSettingsLabel = resolvedKeybinding.getUserSettingsLabel();
if (typeof expectedUserSettingsLabel === 'string' && !KeybindingEditorDecorationsRenderer._userSettingsFuzzyEquals(value.value, expectedUserSettingsLabel)) {
return this._createDecoration(false, resolvedKeybinding.getLabel(), usLabel, model, value);
}
return null;
}
return null;
}
static _userSettingsFuzzyEquals(a: string, b: string): boolean {
a = a.trim().toLowerCase();
b = b.trim().toLowerCase();
if (a === b) {
return true;
}
const aParts = KeybindingParser.parseUserBinding(a);
const bParts = KeybindingParser.parseUserBinding(b);
if (aParts.length !== bParts.length) {
return false;
}
for (let i = 0, len = aParts.length; i < len; i++) {
if (!this._userBindingEquals(aParts[i], bParts[i])) {
return false;
}
}
return true;
}
private static _userBindingEquals(a: SimpleKeybinding | ScanCodeBinding, b: SimpleKeybinding | ScanCodeBinding): boolean {
if (a === null && b === null) {
return true;
}
if (!a || !b) {
return false;
}
if (a instanceof SimpleKeybinding && b instanceof SimpleKeybinding) {
return a.equals(b);
}
if (a instanceof ScanCodeBinding && b instanceof ScanCodeBinding) {
return a.equals(b);
}
return false;
}
private _createDecoration(isError: boolean, uiLabel: string | null, usLabel: string | null, model: ITextModel, keyNode: Node): IModelDeltaDecoration {
let msg: MarkdownString;
let className: string;
let beforeContentClassName: string;
let overviewRulerColor: ThemeColor;
if (isError) {
// this is the error case
msg = new MarkdownString().appendText(NLS_KB_LAYOUT_ERROR_MESSAGE);
className = 'keybindingError';
beforeContentClassName = 'inlineKeybindingError';
overviewRulerColor = themeColorFromId(overviewRulerError);
} else {
// this is the info case
if (usLabel && uiLabel !== usLabel) {
msg = new MarkdownString(
nls.localize({
key: 'defineKeybinding.kbLayoutLocalAndUSMessage',
comment: [
'Please translate maintaining the stars (*) around the placeholders such that they will be rendered in bold.',
'The placeholders will contain a keyboard combination e.g. Ctrl+Shift+/'
]
}, "**{0}** for your current keyboard layout (**{1}** for US standard).", uiLabel, usLabel)
);
} else {
msg = new MarkdownString(
nls.localize({
key: 'defineKeybinding.kbLayoutLocalMessage',
comment: [
'Please translate maintaining the stars (*) around the placeholder such that it will be rendered in bold.',
'The placeholder will contain a keyboard combination e.g. Ctrl+Shift+/'
]
}, "**{0}** for your current keyboard layout.", uiLabel)
);
}
className = 'keybindingInfo';
beforeContentClassName = 'inlineKeybindingInfo';
overviewRulerColor = themeColorFromId(overviewRulerInfo);
}
const startPosition = model.getPositionAt(keyNode.offset);
const endPosition = model.getPositionAt(keyNode.offset + keyNode.length);
const range = new Range(
startPosition.lineNumber, startPosition.column,
endPosition.lineNumber, endPosition.column
);
// icon + highlight + message decoration
return {
range: range,
options: {
stickiness: TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges,
className: className,
beforeContentClassName: beforeContentClassName,
hoverMessage: msg,
overviewRuler: {
color: overviewRulerColor,
position: OverviewRulerLane.Right
}
}
};
}
}
class DefineKeybindingCommand extends EditorCommand {
static readonly ID = 'editor.action.defineKeybinding';
constructor() {
super({
id: DefineKeybindingCommand.ID,
precondition: ContextKeyExpr.and(EditorContextKeys.writable, EditorContextKeys.languageId.isEqualTo('jsonc')),
kbOpts: {
kbExpr: EditorContextKeys.editorTextFocus,
primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.KEY_K),
weight: KeybindingWeight.EditorContrib
}
});
}
runEditorCommand(accessor: ServicesAccessor, editor: ICodeEditor): void {
if (!isInterestingEditorModel(editor) || editor.getConfiguration().readOnly) {
return;
}
const controller = DefineKeybindingController.get(editor);
if (controller && controller.keybindingWidgetRenderer) {
controller.keybindingWidgetRenderer.showDefineKeybindingWidget();
}
}
}
function isInterestingEditorModel(editor: ICodeEditor): boolean {
const model = editor.getModel();
if (!model) {
return false;
}
const url = model.uri.toString();
return INTERESTING_FILE.test(url);
}
registerEditorContribution(DefineKeybindingController);
registerEditorCommand(new DefineKeybindingCommand());

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="3 3 16 16" enable-background="new 3 3 16 16"><polygon fill="#e8e8e8" points="12.597,11.042 15.4,13.845 13.844,15.4 11.042,12.598 8.239,15.4 6.683,13.845 9.485,11.042 6.683,8.239 8.238,6.683 11.042,9.486 13.845,6.683 15.4,8.239"/></svg>

After

Width:  |  Height:  |  Size: 307 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="3 3 16 16" enable-background="new 3 3 16 16"><polygon fill="#424242" points="12.597,11.042 15.4,13.845 13.844,15.4 11.042,12.598 8.239,15.4 6.683,13.845 9.485,11.042 6.683,8.239 8.238,6.683 11.042,9.486 13.845,6.683 15.4,8.239"/></svg>

After

Width:  |  Height:  |  Size: 307 B

View File

@@ -0,0 +1 @@
<svg width="16" height="16" xmlns="http://www.w3.org/2000/svg"><title>Layer 1</title><rect height="11" width="3" y="3" x="7" fill="#424242"/><rect height="3" width="11" y="7" x="3" fill="#424242"/></svg>

After

Width:  |  Height:  |  Size: 203 B

View File

@@ -0,0 +1 @@
<svg width="16" height="16" xmlns="http://www.w3.org/2000/svg"><title>Layer 1</title><rect height="11" width="3" y="3" x="7" fill="#C5C5C5"/><rect height="3" width="11" y="7" x="3" fill="#C5C5C5"/></svg>

After

Width:  |  Height:  |  Size: 203 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="-0.994 0 16 16" enable-background="new -0.994 0 16 16"><path fill="#C5C5C5" d="M13 6c0 1.461-.636 2.846-1.746 3.797l-5.584 4.951-1.324-1.496 5.595-4.962c.678-.582 1.061-1.413 1.061-2.29 0-1.654-1.345-3-2.997-3-.71 0-1.399.253-1.938.713l-1.521 1.287h2.448l-1.998 2h-3.996v-4l1.998-2v2.692l1.775-1.504c.899-.766 2.047-1.188 3.232-1.188 2.754 0 4.995 2.243 4.995 5z"/></svg>

After

Width:  |  Height:  |  Size: 443 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="-0.994 0 16 16" enable-background="new -0.994 0 16 16"><path fill="#424242" d="M13 6c0 1.461-.636 2.846-1.746 3.797l-5.584 4.951-1.324-1.496 5.595-4.962c.678-.582 1.061-1.413 1.061-2.29 0-1.654-1.345-3-2.997-3-.71 0-1.399.253-1.938.713l-1.521 1.287h2.448l-1.998 2h-3.996v-4l1.998-2v2.692l1.775-1.504c.899-.766 2.047-1.188 3.232-1.188 2.754 0 4.995 2.243 4.995 5z"/></svg>

After

Width:  |  Height:  |  Size: 443 B

View File

@@ -0,0 +1 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M7.065 13H15v2H2.056v-2h5.009zm3.661-12H7.385L8.44 2.061 7.505 3H15V1h-4.274zM3.237 9H2.056v2H15V9H3.237zm4.208-4l.995 1-.995 1H15V5H7.445z" fill="#C5C5C5"/><path d="M5.072 4.03L7.032 6 5.978 7.061l-1.96-1.97-1.961 1.97L1 6l1.96-1.97L1 2.061 2.056 1l1.96 1.97L5.977 1l1.057 1.061L5.072 4.03z" fill="#F48771"/></svg>

After

Width:  |  Height:  |  Size: 419 B

View File

@@ -0,0 +1 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M7.065 13H15v2H2.056v-2h5.009zm3.661-12H7.385L8.44 2.061 7.505 3H15V1h-4.274zM3.237 9H2.056v2H15V9H3.237zm4.208-4l.995 1-.995 1H15V5H7.445z" fill="#424242"/><path d="M5.072 4.03L7.032 6 5.978 7.061l-1.96-1.97-1.961 1.97L1 6l1.96-1.97L1 2.061 2.056 1l1.96 1.97L5.977 1l1.057 1.061L5.072 4.03z" fill="#A1260D"/></svg>

After

Width:  |  Height:  |  Size: 419 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="-1 0 16 16" enable-background="new -1 0 16 16"><path fill="#424242" d="M14 1v9h-1v-8h-8v-1h9zm-11 2v1h8v8h1v-9h-9zm7 2v9h-9v-9h9zm-2 2h-5v5h5v-5z"/><rect x="4" y="9" fill="#00539C" width="3" height="1"/></svg>

After

Width:  |  Height:  |  Size: 281 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="-1 0 16 16" enable-background="new -1 0 16 16"><path fill="#C5C5C5" d="M14 1v9h-1v-8h-8v-1h9zm-11 2v1h8v8h1v-9h-9zm7 2v9h-9v-9h9zm-2 2h-5v5h5v-5z"/><rect x="4" y="9" fill="#75BEFF" width="3" height="1"/></svg>

After

Width:  |  Height:  |  Size: 281 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><path fill="#E8E8E8" d="M6 4v8l4-4-4-4zm1 2.414L8.586 8 7 9.586V6.414z"/></svg>

After

Width:  |  Height:  |  Size: 139 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><path fill="#646465" d="M6 4v8l4-4-4-4zm1 2.414L8.586 8 7 9.586V6.414z"/></svg>

After

Width:  |  Height:  |  Size: 139 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"><style type="text/css">.icon-canvas-transparent{opacity:0;fill:#F6F6F6;} .icon-vs-out{fill:#F6F6F6;} .icon-vs-bg{fill:#424242;}</style><path class="icon-canvas-transparent" d="M16 16h-16v-16h16v16z" id="canvas"/><path class="icon-vs-out" d="M16 4.28l-11.673 11.72h-4.327v-4.406l11.477-11.594h.308l4.215 4.237v.043z" id="outline" style="display: none;"/><path class="icon-vs-bg" d="M14.598 4.25l-1.688 1.75-3-3 1.688-1.75 3 3zm-5.688-.25l-7 7 3 3 7-7-3-3zm-7.91 8.09v2.91h2.91l-2.91-2.91z" id="iconBg"/></svg>

After

Width:  |  Height:  |  Size: 571 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"><style type="text/css">.icon-canvas-transparent{opacity:0;fill:#2d2d30;} .icon-vs-out{fill:#2d2d30;} .icon-vs-bg{fill:#c5c5c5;}</style><path class="icon-canvas-transparent" d="M16 16h-16v-16h16v16z" id="canvas"/><path class="icon-vs-out" d="M16 4.28l-11.673 11.72h-4.327v-4.406l11.477-11.594h.308l4.215 4.237v.043z" id="outline" style="display: none;"/><path class="icon-vs-bg" d="M14.598 4.25l-1.688 1.75-3-3 1.688-1.75 3 3zm-5.688-.25l-7 7 3 3 7-7-3-3zm-7.91 8.09v2.91h2.91l-2.91-2.91z" id="iconBg"/></svg>

After

Width:  |  Height:  |  Size: 571 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><defs><style>.icon-canvas-transparent,.icon-vs-out{fill:#252526;}.icon-canvas-transparent{opacity:0;}.icon-vs-bg{fill:#c5c5c5;}</style></defs><title>Ellipsis_bold_16x</title><g id="canvas"><path class="icon-canvas-transparent" d="M16,0V16H0V0Z"/></g><g id="outline" style="display: none;"><path class="icon-vs-out" d="M6,7.5A2.5,2.5,0,1,1,3.5,5,2.5,2.5,0,0,1,6,7.5ZM8.5,5A2.5,2.5,0,1,0,11,7.5,2.5,2.5,0,0,0,8.5,5Zm5,0A2.5,2.5,0,1,0,16,7.5,2.5,2.5,0,0,0,13.5,5Z" style="display: none;"/></g><g id="iconBg"><path class="icon-vs-bg" d="M5,7.5A1.5,1.5,0,1,1,3.5,6,1.5,1.5,0,0,1,5,7.5ZM8.5,6A1.5,1.5,0,1,0,10,7.5,1.5,1.5,0,0,0,8.5,6Zm5,0A1.5,1.5,0,1,0,15,7.5,1.5,1.5,0,0,0,13.5,6Z"/></g></svg>

After

Width:  |  Height:  |  Size: 748 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><defs><style>.icon-canvas-transparent,.icon-vs-out{fill:#f6f6f6;}.icon-canvas-transparent{opacity:0;}.icon-vs-bg{fill:#424242;}</style></defs><title>Ellipsis_bold_16x</title><g id="canvas"><path class="icon-canvas-transparent" d="M16,0V16H0V0Z"/></g><g id="outline" style="display: none;"><path class="icon-vs-out" d="M6,7.5A2.5,2.5,0,1,1,3.5,5,2.5,2.5,0,0,1,6,7.5ZM8.5,5A2.5,2.5,0,1,0,11,7.5,2.5,2.5,0,0,0,8.5,5Zm5,0A2.5,2.5,0,1,0,16,7.5,2.5,2.5,0,0,0,13.5,5Z" style="display: none;"/></g><g id="iconBg"><path class="icon-vs-bg" d="M5,7.5A1.5,1.5,0,1,1,3.5,6,1.5,1.5,0,0,1,5,7.5ZM8.5,6A1.5,1.5,0,1,0,10,7.5,1.5,1.5,0,0,0,8.5,6Zm5,0A1.5,1.5,0,1,0,15,7.5,1.5,1.5,0,0,0,13.5,6Z"/></g></svg>

After

Width:  |  Height:  |  Size: 748 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><path fill="#E8E8E8" d="M11 10H5.344L11 4.414V10z"/></svg>

After

Width:  |  Height:  |  Size: 118 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><path fill="#646465" d="M11 10H5.344L11 4.414V10z"/></svg>

After

Width:  |  Height:  |  Size: 118 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"><path d="M8 1c-3.865 0-7 3.135-7 7s3.135 7 7 7 7-3.135 7-7-3.135-7-7-7zm1 12h-2v-7h2v7zm0-8h-2v-2h2v2z" fill="#1BA1E2"/><path d="M7 6h2v7h-2v-7zm0-1h2v-2h-2v2z" fill="#fff"/></svg>

After

Width:  |  Height:  |  Size: 243 B

View File

@@ -0,0 +1,72 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
.defineKeybindingWidget {
padding: 10px;
position: absolute;
}
.defineKeybindingWidget .message {
width: 400px;
text-align: center;
}
.defineKeybindingWidget .monaco-inputbox,
.defineKeybindingWidget .output,
.defineKeybindingWidget .existing {
margin-top:10px;
width: 400px;
display: block;
text-align: center;
}
.defineKeybindingWidget .input {
text-align: center;
}
.defineKeybindingWidget .output {
display: flex;
justify-content: center;
}
.defineKeybindingWidget .existing .existingText {
text-decoration: underline;
cursor: pointer;
}
.defineKeybindingWidget .output .monaco-keybinding {
margin: 0px 4px;
}
/* Editor decorations */
.monaco-editor .inlineKeybindingInfo:before {
margin: 0.2em 0.1em 0 0.1em;
content:" ";
display:inline-block;
height:0.8em;
width:1em;
background: url(info.svg) 0px -0.1em no-repeat;
background-size: 0.9em;
}
.monaco-editor .inlineKeybindingError:before {
margin: 0.1em 0.1em 0 0.1em;
content:" ";
display:inline-block;
height:0.8em;
width:1em;
background: url(status-error.svg) 0px -0.1em no-repeat;
background-size: 1em;
}
.monaco-editor .keybindingInfo {
box-shadow: inset 0 0 0 1px #B9B9B9;
background-color: rgba(100, 100, 250, 0.2);
}
.monaco-editor .keybindingError {
box-shadow: inset 0 0 0 1px #B9B9B9;
background-color: rgba(250, 100, 100, 0.2);
}

View File

@@ -0,0 +1,227 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
.keybindings-editor {
padding: 11px 0px 0px 27px;
}
/* header styling */
.keybindings-editor > .keybindings-header {
padding: 0px 10px 11px 0;
}
.keybindings-editor > .keybindings-header > .search-container {
position: relative;
}
.keybindings-editor > .keybindings-header > .search-container > .keybindings-search-actions-container {
position: absolute;
top: 0;
right: 10px;
margin-top: 5px;
display: flex;
}
.keybindings-editor > .keybindings-header > .search-container > .keybindings-search-actions-container > .recording-badge {
margin-right: 8px;
padding: 0px 8px;
border-radius: 2px;
}
.keybindings-editor > .keybindings-header.small > .search-container > .keybindings-search-actions-container > .recording-badge,
.keybindings-editor > .keybindings-header > .search-container > .keybindings-search-actions-container > .recording-badge.disabled {
display: none;
}
.keybindings-editor > .keybindings-header > .search-container > .keybindings-search-actions-container > .monaco-action-bar .action-item > .icon {
width:16px;
height: 18px;
}
.keybindings-editor > .keybindings-header > .search-container > .keybindings-search-actions-container > .monaco-action-bar .action-item {
margin-right: 4px;
}
.keybindings-editor .monaco-action-bar .action-item > .sort-by-precedence {
background: url('sort_precedence.svg') center center no-repeat;
}
.hc-black .keybindings-editor .monaco-action-bar .action-item > .sort-by-precedence,
.vs-dark .keybindings-editor .monaco-action-bar .action-item > .sort-by-precedence {
background: url('sort_precedence_inverse.svg') center center no-repeat;
}
.keybindings-editor .monaco-action-bar .action-item > .record-keys {
background: url('record-keys.svg') center center no-repeat;
}
.hc-black .keybindings-editor .monaco-action-bar .action-item > .record-keys,
.vs-dark .keybindings-editor .monaco-action-bar .action-item > .record-keys {
background: url('record-keys-inverse.svg') center center no-repeat;
}
.keybindings-editor .monaco-action-bar .action-item > .clear-input {
background: url('clear.svg') center center no-repeat;
}
.hc-black .keybindings-editor .monaco-action-bar .action-item > .clear-input,
.vs-dark .keybindings-editor .monaco-action-bar .action-item > .clear-input {
background: url('clear-inverse.svg') center center no-repeat;
}
.keybindings-editor > .keybindings-header .open-keybindings-container {
margin-top: 10px;
display: flex;
}
.keybindings-editor > .keybindings-header .open-keybindings-container > div {
opacity: 0.7;
}
.keybindings-editor > .keybindings-header .open-keybindings-container > .file-name {
text-decoration: underline;
cursor: pointer;
margin-left: 4px;
}
.keybindings-editor > .keybindings-header .open-keybindings-container > .file-name:focus {
opacity: 1;
}
/** List based styling **/
.keybindings-editor > .keybindings-body > .keybindings-list-header,
.keybindings-editor > .keybindings-body .keybindings-list-container {
width: 100%;
border-spacing: 0;
border-collapse: separate;
}
.keybindings-editor > .keybindings-body > .keybindings-list-header,
.keybindings-editor > .keybindings-body > .keybindings-list-container .monaco-list-row {
cursor: default;
display: flex;
}
.keybindings-editor > .keybindings-body > .keybindings-list-header,
.keybindings-editor > .keybindings-body > .keybindings-list-container .monaco-list-row.odd:not(.focused):not(.selected):not(:hover),
.keybindings-editor > .keybindings-body > .keybindings-list-container .monaco-list:not(:focus) .monaco-list-row.focused.odd:not(.selected):not(:hover),
.keybindings-editor > .keybindings-body > .keybindings-list-container .monaco-list:not(.focused) .monaco-list-row.focused.odd:not(.selected):not(:hover) {
background-color: rgba(130, 130, 130, 0.04);
}
.keybindings-editor > .keybindings-body > .keybindings-list-header > .header {
text-align: left;
font-weight: bold;
}
.keybindings-editor > .keybindings-body > .keybindings-list-header > .header,
.keybindings-editor > .keybindings-body > .keybindings-list-container .monaco-list-row .column {
align-items: center;
display: flex;
overflow: hidden;
}
.keybindings-editor > .keybindings-body > .keybindings-list-container .monaco-list-row .command.vertical-align-column {
flex-direction: column;
align-items: flex-start;
justify-content: center;
}
.keybindings-editor > .keybindings-body > .keybindings-list-container .monaco-list-row .command .command-default-label {
opacity: 0.8;
margin-top: 2px;
}
.keybindings-editor > .keybindings-body > .keybindings-list-container .monaco-list-row .keybinding .monaco-highlighted-label {
padding-left: 10px;
}
.keybindings-editor > .keybindings-body > .keybindings-list-container .monaco-list-row > .when .empty {
padding-left: 4px;
}
.keybindings-editor > .keybindings-body > .keybindings-list-container .monaco-list-row > .when:not(.input-mode) .monaco-inputbox,
.keybindings-editor > .keybindings-body > .keybindings-list-container .monaco-list-row > .when.input-mode .when-label {
display: none;
}
.keybindings-editor > .keybindings-body > .keybindings-list-container .monaco-list-row > .when .monaco-inputbox {
width: 100%;
line-height: normal;
}
.monaco-workbench.mac .keybindings-editor > .keybindings-body > .keybindings-list-container .monaco-list-row > .when .monaco-inputbox,
.monaco-workbench.mac .keybindings-editor > .keybindings-body > .keybindings-list-container .monaco-list-row > .when .monaco-inputbox {
height: 24px;
}
.keybindings-editor > .keybindings-body > .keybindings-list-container .monaco-list-row .command .monaco-highlighted-label,
.keybindings-editor > .keybindings-body > .keybindings-list-container .monaco-list-row .source .monaco-highlighted-label,
.keybindings-editor > .keybindings-body > .keybindings-list-container .monaco-list-row .when .monaco-highlighted-label {
overflow: hidden;
text-overflow: ellipsis;
}
.keybindings-editor > .keybindings-body > .keybindings-list-container .monaco-list-row > .column > .code {
font-family: var(--monaco-monospace-font);
font-size: 90%;
opacity: 0.8;
display: flex;
overflow: hidden;
}
.keybindings-editor > .keybindings-body > .keybindings-list-container .monaco-list-row > .column > .code.strong {
padding: 1px 4px;
background-color: rgba(128, 128, 128, 0.17);
border-radius: 4px;
}
.keybindings-editor > .keybindings-body > .keybindings-list-container .monaco-list-row > .column .highlight {
font-weight: bold;
}
.keybindings-editor > .keybindings-body > .keybindings-list-container .monaco-list:focus .monaco-list-row.selected > .column .highlight,
.keybindings-editor > .keybindings-body > .keybindings-list-container .monaco-list:focus .monaco-list-row.selected.focused > .column .highlight {
color: inherit;
}
.keybindings-editor > .keybindings-body > .keybindings-list-container .monaco-list-row > .column.actions .monaco-action-bar {
display: none;
flex: 1;
}
.keybindings-editor > .keybindings-body > .keybindings-list-container .monaco-list-row.selected > .column.actions .monaco-action-bar,
.keybindings-editor > .keybindings-body > .keybindings-list-container .monaco-list.focused .monaco-list-row.focused > .column.actions .monaco-action-bar,
.keybindings-editor > .keybindings-body > .keybindings-list-container .monaco-list-row:hover > .column.actions .monaco-action-bar {
display: flex;
}
.keybindings-editor > .keybindings-body > .keybindings-list-container .monaco-list-row > .column .monaco-action-bar .action-item > .icon {
width:16px;
height: 16px;
cursor: pointer;
margin-top: 3px;
}
.keybindings-editor > .keybindings-body > .keybindings-list-container .monaco-list-row > .column .monaco-action-bar .action-item > .icon.edit {
background: url('edit.svg') center center no-repeat;
transform: rotate(-90deg);
}
.hc-black .keybindings-editor > .keybindings-body > .keybindings-list-container .monaco-list-row > .column .monaco-action-bar .action-item > .icon.edit,
.vs-dark .keybindings-editor > .keybindings-body > .keybindings-list-container .monaco-list-row > .column .monaco-action-bar .action-item > .icon.edit {
background: url('edit_inverse.svg') center center no-repeat;
}
.keybindings-editor > .keybindings-body > .keybindings-list-container .monaco-list-row > .column .monaco-action-bar .action-item > .icon.add {
background: url('add.svg') center center no-repeat;
}
.hc-black .keybindings-editor > .keybindings-body > .keybindings-list-container .monaco-list-row > .column .monaco-action-bar .action-item > .icon.add,
.vs-dark .keybindings-editor > .keybindings-body > .keybindings-list-container .monaco-list-row > .column .monaco-action-bar .action-item > .icon.add {
background: url('add_inverse.svg') center center no-repeat;
}

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"><polygon fill="#C5C5C5" points="10,2 7.414,2 8.414,3 9,3 9,3.586 9,4 9,4.414 9,6 12,6 12,13 4,13 4,8 3,8 3,14 13,14 13,5"/><polygon fill="#75BEFF" points="5,1 3,1 5,3 1,3 1,5 5,5 3,7 5,7 8,4"/></svg>

After

Width:  |  Height:  |  Size: 262 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"><polygon fill="#656565" points="10,2 7.414,2 8.414,3 9,3 9,3.586 9,4 9,4.414 9,6 12,6 12,13 4,13 4,8 3,8 3,14 13,14 13,5"/><polygon fill="#00539C" points="5,1 3,1 5,3 1,3 1,5 5,5 3,7 5,7 8,4"/></svg>

After

Width:  |  Height:  |  Size: 262 B

View File

@@ -0,0 +1,266 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
.preferences-editor > .preferences-header {
padding-left: 27px;
padding-right: 32px;
padding-bottom: 11px;
padding-top: 11px;
}
.preferences-editor > .preferences-editors-container.side-by-side-preferences-editor {
position: relative;
}
.preferences-editor > .preferences-editors-container.side-by-side-preferences-editor .preferences-header-container {
line-height: 28px;
}
.settings-tabs-widget > .monaco-action-bar .action-item.disabled {
display: none;
}
.settings-tabs-widget > .monaco-action-bar .action-item {
max-width: 300px;
overflow: hidden;
text-overflow: ellipsis;
}
.default-preferences-editor-container > .preferences-header-container > .default-preferences-header,
.settings-tabs-widget > .monaco-action-bar .action-item .action-label {
text-transform: uppercase;
font-size: 11px;
margin-right: 5px;
cursor: pointer;
display: flex;
overflow: hidden;
text-overflow: ellipsis;
}
.default-preferences-editor-container > .preferences-header-container > .default-preferences-header,
.preferences-editor .settings-tabs-widget > .monaco-action-bar .action-item .action-label {
margin-left: 33px;
}
.settings-tabs-widget > .monaco-action-bar .action-item .action-label {
display: block;
}
.settings-tabs-widget > .monaco-action-bar .action-item .action-label.folder-settings {
display: flex;
}
.settings-tabs-widget > .monaco-action-bar .actions-container {
justify-content: flex-start;
}
.default-preferences-editor-container > .preferences-header-container > .default-preferences-header,
.settings-tabs-widget > .monaco-action-bar .action-item {
padding: 3px 0px;
}
.settings-tabs-widget > .monaco-action-bar .action-item .action-title {
text-overflow: ellipsis;
overflow: hidden;
}
.settings-tabs-widget > .monaco-action-bar .action-item .action-details {
text-transform: none;
margin-left: 0.5em;
font-size: 10px;
opacity: 0.7;
}
.settings-tabs-widget .monaco-action-bar .action-item .dropdown-icon {
padding-left: 0.3em;
padding-top: 8px;
font-size: 12px;
}
.settings-tabs-widget .monaco-action-bar .action-item .dropdown-icon.hide {
display: none;
}
.preferences-header > .settings-header-widget {
flex: 1;
display: flex;
position: relative;
align-self: stretch;
}
.settings-header-widget > .settings-search-controls > .settings-count-widget {
margin: 6px 0px;
padding: 0px 8px;
border-radius: 2px;
float: left;
}
.settings-header-widget > .settings-search-controls {
position: absolute;
right: 10px;
}
.settings-header-widget > .settings-search-controls > .settings-count-widget.hide {
display: none;
}
.settings-header-widget > .settings-search-container {
flex: 1;
}
.settings-header-widget > .settings-search-container > .settings-search-input {
vertical-align: middle;
}
.settings-header-widget > .settings-search-container > .settings-search-input > .monaco-inputbox {
height: 30px;
}
.vs .settings-header-widget > .settings-search-container > .settings-search-input > .monaco-inputbox {
border: 1px solid #ddd;
}
.settings-header-widget > .settings-search-container > .settings-search-input > .monaco-inputbox .input {
font-size: 14px;
padding-left:10px;
}
.monaco-editor .view-zones > .settings-header-widget {
z-index: 1;
}
.monaco-editor .settings-header-widget .title-container {
display: flex;
user-select: none;
}
.vs .monaco-editor .settings-header-widget .title-container {
color: #6f6f6f;
}
.vs-dark .monaco-editor .settings-header-widget .title-container {
color: #bbbbbb;
}
.hc-black .monaco-editor .settings-header-widget .title-container {
color: white;
}
.monaco-editor .settings-header-widget .title-container .title {
font-weight: bold;
white-space: nowrap;
text-transform: uppercase;
}
.monaco-editor .settings-header-widget .title-container .message {
white-space: nowrap;
}
.monaco-editor .settings-group-title-widget {
z-index: 1;
}
.monaco-editor .settings-group-title-widget .title-container {
width: 100%;
cursor: pointer;
font-weight: bold;
user-select: none;
display: flex;
}
.vs .monaco-editor .settings-group-title-widget .title-container {
color: #6f6f6f;
}
.monaco-editor .settings-group-title-widget .title-container .title {
white-space: nowrap;
overflow: hidden;
}
.vs-dark .monaco-editor .settings-group-title-widget .title-container {
color: #bbbbbb;
}
.hc-black .monaco-editor .settings-group-title-widget .title-container {
color: white;
}
.monaco-editor.vs-dark .settings-group-title-widget .title-container.focused,
.monaco-editor.vs .settings-group-title-widget .title-container.focused {
outline: none !important;
}
.monaco-editor .settings-group-title-widget .title-container.focused,
.monaco-editor .settings-group-title-widget .title-container:hover {
background-color: rgba(153, 153, 153, 0.2);
}
.monaco-editor.hc-black .settings-group-title-widget .title-container.focused {
outline: 1px dotted #f38518;
}
.monaco-editor .settings-group-title-widget .title-container .expand-collapse-icon {
background: url(expanded.svg) 50% 50% no-repeat;
width: 16px;
height: 100%;
}
.monaco-editor.vs-dark .settings-group-title-widget .title-container .expand-collapse-icon,
.monaco-editor.hc-black .settings-group-title-widget .title-container .expand-collapse-icon {
background: url(expanded-dark.svg) 50% 50% no-repeat;
}
.monaco-editor .settings-group-title-widget .title-container.collapsed .expand-collapse-icon {
background: url(collapsed.svg) 50% 50% no-repeat;
}
.monaco-editor.vs-dark .settings-group-title-widget .title-container.collapsed .expand-collapse-icon,
.monaco-editor.hc-black .settings-group-title-widget .title-container.collapsed .expand-collapse-icon {
background: url(collapsed-dark.svg) 50% 50% no-repeat;
}
.monaco-editor .edit-preferences-widget {
background: url('edit.svg') center center no-repeat;
transform: rotate(-90deg);
width:16px;
height: 16px;
cursor: pointer;
}
.monaco-editor .edit-preferences-widget.hidden {
display: none;
visibility: hidden;
}
.monaco-editor.hc-black .edit-preferences-widget,
.monaco-editor.vs-dark .edit-preferences-widget {
background: url('edit_inverse.svg') center center no-repeat;
}
.monaco-editor .unsupportedWorkbenhSettingInfo:before {
content:" ";
display:inline-block;
height:1em;
width:1em;
background: url(info.svg) 50% 50% no-repeat;
background-size: 0.9em;
}
.monaco-editor .dim-configuration {
color: #b1b1b1;
}
.monaco-editor .floating-click-widget {
padding: 10px;
border-radius: 5px;
cursor: pointer;
}
.title-actions .action-item .icon.collapseAll,
.editor-actions .action-item .icon.collapseAll {
background: url('collapseAll.svg') center center no-repeat;
}
.vs-dark .title-actions .action-item .icon.collapseAll,
.vs-dark .editor-actions .action-item .icon.collapseAll {
background: url('collapseAll_inverse.svg') center center no-repeat;
}

View File

@@ -0,0 +1,3 @@
<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="M10 5H9V4H10V5V5ZM3 6H2V7H3V6V6ZM8 4H7V5H8V4V4ZM4 4H2V5H4V4V4ZM12 11H14V10H12V11V11ZM8 7H9V6H8V7V7ZM4 10H2V11H4V10V10ZM12 4H11V5H12V4V4ZM14 4H13V5H14V4V4ZM12 9H14V6H12V9V9ZM16 3V12C16 12.55 15.55 13 15 13H1C0.45 13 0 12.55 0 12V3C0 2.45 0.45 2 1 2H15C15.55 2 16 2.45 16 3V3ZM15 3H1V12H15V3V3ZM6 7H7V6H6V7V7ZM6 4H5V5H6V4V4ZM4 7H5V6H4V7V7ZM5 11H11V10H5V11V11ZM10 7H11V6H10V7V7ZM3 8H2V9H3V8V8ZM8 8V9H9V8H8V8ZM6 8V9H7V8H6V8ZM5 8H4V9H5V8V8ZM10 9H11V8H10V9V9Z" fill="#C5C5C5"/>
</svg>

After

Width:  |  Height:  |  Size: 624 B

View File

@@ -0,0 +1,3 @@
<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="M10 5H9V4H10V5V5ZM3 6H2V7H3V6V6ZM8 4H7V5H8V4V4ZM4 4H2V5H4V4V4ZM12 11H14V10H12V11V11ZM8 7H9V6H8V7V7ZM4 10H2V11H4V10V10ZM12 4H11V5H12V4V4ZM14 4H13V5H14V4V4ZM12 9H14V6H12V9V9ZM16 3V12C16 12.55 15.55 13 15 13H1C0.45 13 0 12.55 0 12V3C0 2.45 0.45 2 1 2H15C15.55 2 16 2.45 16 3V3ZM15 3H1V12H15V3V3ZM6 7H7V6H6V7V7ZM6 4H5V5H6V4V4ZM4 7H5V6H4V7V7ZM5 11H11V10H5V11V11ZM10 7H11V6H10V7V7ZM3 8H2V9H3V8V8ZM8 8V9H9V8H8V8ZM6 8V9H7V8H6V8ZM5 8H4V9H5V8V8ZM10 9H11V8H10V9V9Z" fill="#424242"/>
</svg>

After

Width:  |  Height:  |  Size: 624 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"><polygon fill="#2d2d30" points="13.64,7.396 12.169,2.898 10.706,3.761 11.087,2 6.557,2 6.936,3.762 5.473,2.898 4,7.396 5.682,7.554 4.513,8.561 5.013,9 2,9 2,14 7,14 7,10.747 7.978,11.606 8.82,9.725 9.661,11.602 13.144,8.562 11.968,7.554"/><g fill="#C5C5C5"><path d="M12.301 6.518l-2.772.262 2.086 1.788-1.594 1.392-1.201-2.682-1.201 2.682-1.583-1.392 2.075-1.788-2.771-.262.696-2.126 2.358 1.392-.599-2.784h2.053l-.602 2.783 2.359-1.392.696 2.127z"/><rect x="3" y="10" width="3" height="3"/></g></svg>

After

Width:  |  Height:  |  Size: 564 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"><polygon fill="#F6F6F6" points="13.64,7.396 12.169,2.898 10.706,3.761 11.087,2 6.557,2 6.936,3.762 5.473,2.898 4,7.396 5.682,7.554 4.513,8.561 5.013,9 2,9 2,14 7,14 7,10.747 7.978,11.606 8.82,9.725 9.661,11.602 13.144,8.562 11.968,7.554"/><g fill="#424242"><path d="M12.301 6.518l-2.772.262 2.086 1.788-1.594 1.392-1.201-2.682-1.201 2.682-1.583-1.392 2.075-1.788-2.771-.262.696-2.126 2.358 1.392-.599-2.784h2.053l-.602 2.783 2.359-1.392.696 2.127z"/><rect x="3" y="10" width="3" height="3"/></g></svg>

After

Width:  |  Height:  |  Size: 564 B

View File

@@ -0,0 +1,107 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
.settings-editor > .settings-body > .settings-tree-container .setting-item.setting-item-exclude .setting-item-value > .setting-item-control {
width: 100%;
}
.settings-editor > .settings-body > .settings-tree-container .setting-item.setting-item-exclude .setting-exclude-pattern {
margin-right: 3px;
margin-left: 2px;
}
.settings-editor > .settings-body > .settings-tree-container .setting-item.setting-item-exclude .setting-exclude-pattern,
.settings-editor > .settings-body > .settings-tree-container .setting-item.setting-item-exclude .setting-exclude-sibling {
display: inline-block;
line-height: 22px;
font-family: var(--monaco-monospace-font);
}
.settings-editor > .settings-body > .settings-tree-container .setting-item.setting-item-exclude .setting-exclude-sibling {
opacity: 0.7;
margin-left: 0.5em;
font-size: 0.9em;
white-space: pre;
}
.settings-editor > .settings-body > .settings-tree-container .setting-item.setting-item-exclude .setting-exclude-row .monaco-action-bar {
display: none;
position: absolute;
right: 0px;
margin-top: 1px;
}
.settings-editor > .settings-body > .settings-tree-container .setting-item.setting-item-exclude .setting-exclude-row {
position: relative;
}
.settings-editor > .settings-body > .settings-tree-container .setting-item.setting-item-exclude .setting-exclude-row:focus {
outline: none;
}
.settings-editor > .settings-body > .settings-tree-container .setting-item.setting-item-exclude .setting-exclude-row:hover .monaco-action-bar,
.settings-editor > .settings-body > .settings-tree-container .setting-item.setting-item-exclude .setting-exclude-row.selected .monaco-action-bar {
display: block;
}
.settings-editor > .settings-body > .settings-tree-container .setting-item.setting-item-exclude .setting-exclude-row .monaco-action-bar .action-label {
width: 16px;
height: 16px;
padding: 2px;
margin-right: 2px;
}
.settings-editor > .settings-body > .settings-tree-container .setting-item.setting-item-exclude .setting-exclude-row .monaco-action-bar .setting-excludeAction-edit {
margin-right: 4px;
}
.vs .settings-editor > .settings-body > .settings-tree-container .setting-item.setting-item-exclude .setting-exclude-row .monaco-action-bar .setting-excludeAction-edit {
background: url(edit.svg) center center no-repeat;
}
.vs-dark .settings-editor > .settings-body > .settings-tree-container .setting-item.setting-item-exclude .setting-exclude-row .monaco-action-bar .setting-excludeAction-edit {
background: url(edit_inverse.svg) center center no-repeat;
}
.vs .settings-editor > .settings-body > .settings-tree-container .setting-item.setting-item-exclude .setting-exclude-row .monaco-action-bar .setting-excludeAction-remove {
background: url(action-remove.svg) center center no-repeat;
}
.vs-dark .settings-editor > .settings-body > .settings-tree-container .setting-item.setting-item-exclude .setting-exclude-row .monaco-action-bar .setting-excludeAction-remove {
background: url(action-remove-dark.svg) center center no-repeat;
}
.settings-editor > .settings-body > .settings-tree-container .setting-item.setting-item-exclude .monaco-text-button {
width: initial;
padding: 2px 14px;
}
.settings-editor > .settings-body > .settings-tree-container .setting-item.setting-item-exclude .setting-item-control.setting-exclude-new-mode .setting-exclude-new-row {
display: none;
}
.settings-editor > .settings-body > .settings-tree-container .setting-item.setting-item-exclude .monaco-text-button.setting-exclude-addButton {
margin-right: 10px;
}
.settings-editor > .settings-body > .settings-tree-container .setting-item.setting-item-exclude .setting-exclude-edit-row {
display: flex
}
.settings-editor > .settings-body > .settings-tree-container .setting-item.setting-item-exclude .setting-exclude-patternInput,
.settings-editor > .settings-body > .settings-tree-container .setting-item.setting-item-exclude .setting-exclude-siblingInput {
height: 22px;
max-width: 320px;
flex: 1;
margin-right: 10px;
}
.settings-editor > .settings-body > .settings-tree-container .setting-item.setting-item-exclude .setting-exclude-okButton {
margin-right: 10px;
}
.settings-editor > .settings-body > .settings-tree-container .setting-item.setting-item-exclude .setting-exclude-widget {
margin-bottom: 1px;
}

View File

@@ -0,0 +1 @@
<svg fill="none" height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m5 1v5h-5v9h11v-2.586l1 1 4-4v-8.414zm5 3v2l-2-1.414v-.586zm-2 8h-5v-3h5z" fill="#f6f6f6"/><g fill="#424242"><path d="m3 9h2v2h-2zm3 1h2v2h-2zm8-7v3h.586l.414-.414v-3.586h-9v4h1v-3z"/><path d="m9 10.414v2.586h-7v-5h6v-1h-7v7h9v-2.586zm.414-4.414h.586v-2h-2v.586z"/><path d="m13 5h-2v4l-2-2v2l3 3 3-3v-2l-2 2z"/></g><path d="m8 9.414v-1.414h-6v5h7v-2.586zm-3 1.586h-2v-2h2zm3 1h-2v-2h2zm0-7.414v-.586h6v-1h-7v3h1z" fill="#f0eff1"/></svg>

After

Width:  |  Height:  |  Size: 540 B

View File

@@ -0,0 +1 @@
<svg fill="none" height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m5 1v5h-5v9h11v-2.586l1 1 4-4v-8.414zm5 3v2l-2-1.414v-.586zm-2 8h-5v-3h5z" fill="#2d2d30"/><g fill="#c5c5c5"><path d="m3 9h2v2h-2zm3 1h2v2h-2zm8-7v3h.586l.414-.414v-3.586h-9v4h1v-3z"/><path d="m9 10.414v2.586h-7v-5h6v-1h-7v7h9v-2.586zm.414-4.414h.586v-2h-2v.586z"/><path d="m13 5h-2v4l-2-2v2l3 3 3-3v-2l-2 2z"/></g><path d="m8 9.414v-1.414h-6v5h7v-2.586zm-3 1.586h-2v-2h2zm3 1h-2v-2h2zm0-7.414v-.586h6v-1h-7v3h1z" fill="#2b282e"/></svg>

After

Width:  |  Height:  |  Size: 540 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" enable-background="new 0 0 16 16" height="16" width="16"><circle cx="8" cy="8" r="6" fill="#F6F6F6"/><path d="M8 3C5.238 3 3 5.238 3 8s2.238 5 5 5 5-2.238 5-5-2.238-5-5-5zm3 7l-1 1-2-2-2 2-1-1 2-2.027L5 6l1-1 2 2 2-2 1 1-2 1.973L11 10z" fill="#E51400"/><path fill="#fff" d="M11 6l-1-1-2 2-2-2-1 1 2 1.973L5 10l1 1 2-2 2 2 1-1-2-2.027z"/></svg>

After

Width:  |  Height:  |  Size: 403 B

View File

@@ -0,0 +1,271 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { Action } from 'vs/base/common/actions';
import { dispose, IDisposable } from 'vs/base/common/lifecycle';
import { URI } from 'vs/base/common/uri';
import { getIconClasses } from 'vs/editor/common/services/getIconClasses';
import { IModelService } from 'vs/editor/common/services/modelService';
import { IModeService } from 'vs/editor/common/services/modeService';
import * as nls from 'vs/nls';
import { ICommandService } from 'vs/platform/commands/common/commands';
import { IQuickInputService, IQuickPickItem } from 'vs/platform/quickinput/common/quickInput';
import { IWorkspaceContextService, IWorkspaceFolder, WorkbenchState } from 'vs/platform/workspace/common/workspace';
import { PICK_WORKSPACE_FOLDER_COMMAND_ID } from 'vs/workbench/browser/actions/workspaceCommands';
import { IPreferencesService } from 'vs/workbench/services/preferences/common/preferences';
export class OpenRawDefaultSettingsAction extends Action {
static readonly ID = 'workbench.action.openRawDefaultSettings';
static readonly LABEL = nls.localize('openRawDefaultSettings', "Open Default Settings (JSON)");
constructor(
id: string,
label: string,
@IPreferencesService private readonly preferencesService: IPreferencesService
) {
super(id, label);
}
run(event?: any): Promise<any> {
return this.preferencesService.openRawDefaultSettings();
}
}
export class OpenSettings2Action extends Action {
static readonly ID = 'workbench.action.openSettings2';
static readonly LABEL = nls.localize('openSettings2', "Open Settings (UI)");
constructor(
id: string,
label: string,
@IPreferencesService private readonly preferencesService: IPreferencesService
) {
super(id, label);
}
run(event?: any): Promise<any> {
return this.preferencesService.openSettings(false);
}
}
export class OpenSettingsJsonAction extends Action {
static readonly ID = 'workbench.action.openSettingsJson';
static readonly LABEL = nls.localize('openSettingsJson', "Open Settings (JSON)");
constructor(
id: string,
label: string,
@IPreferencesService private readonly preferencesService: IPreferencesService
) {
super(id, label);
}
run(event?: any): Promise<any> {
return this.preferencesService.openSettings(true);
}
}
export class OpenGlobalSettingsAction extends Action {
static readonly ID = 'workbench.action.openGlobalSettings';
static readonly LABEL = nls.localize('openGlobalSettings', "Open User Settings");
constructor(
id: string,
label: string,
@IPreferencesService private readonly preferencesService: IPreferencesService,
) {
super(id, label);
}
run(event?: any): Promise<any> {
return this.preferencesService.openGlobalSettings();
}
}
export class OpenGlobalKeybindingsAction extends Action {
static readonly ID = 'workbench.action.openGlobalKeybindings';
static readonly LABEL = nls.localize('openGlobalKeybindings', "Open Keyboard Shortcuts");
constructor(
id: string,
label: string,
@IPreferencesService private readonly preferencesService: IPreferencesService
) {
super(id, label);
}
run(event?: any): Promise<any> {
return this.preferencesService.openGlobalKeybindingSettings(false);
}
}
export class OpenGlobalKeybindingsFileAction extends Action {
static readonly ID = 'workbench.action.openGlobalKeybindingsFile';
static readonly LABEL = nls.localize('openGlobalKeybindingsFile', "Open Keyboard Shortcuts (JSON)");
constructor(
id: string,
label: string,
@IPreferencesService private readonly preferencesService: IPreferencesService
) {
super(id, label);
}
run(event?: any): Promise<any> {
return this.preferencesService.openGlobalKeybindingSettings(true);
}
}
export class OpenDefaultKeybindingsFileAction extends Action {
static readonly ID = 'workbench.action.openDefaultKeybindingsFile';
static readonly LABEL = nls.localize('openDefaultKeybindingsFile', "Open Default Keyboard Shortcuts (JSON)");
constructor(
id: string,
label: string,
@IPreferencesService private readonly preferencesService: IPreferencesService
) {
super(id, label);
}
run(event?: any): Promise<any> {
return this.preferencesService.openDefaultKeybindingsFile();
}
}
export class OpenWorkspaceSettingsAction extends Action {
static readonly ID = 'workbench.action.openWorkspaceSettings';
static readonly LABEL = nls.localize('openWorkspaceSettings', "Open Workspace Settings");
private disposables: IDisposable[] = [];
constructor(
id: string,
label: string,
@IPreferencesService private readonly preferencesService: IPreferencesService,
@IWorkspaceContextService private readonly workspaceContextService: IWorkspaceContextService,
) {
super(id, label);
this.update();
this.workspaceContextService.onDidChangeWorkbenchState(() => this.update(), this, this.disposables);
}
private update(): void {
this.enabled = this.workspaceContextService.getWorkbenchState() !== WorkbenchState.EMPTY;
}
run(event?: any): Promise<any> {
return this.preferencesService.openWorkspaceSettings();
}
dispose(): void {
this.disposables = dispose(this.disposables);
super.dispose();
}
}
export const OPEN_FOLDER_SETTINGS_COMMAND = '_workbench.action.openFolderSettings';
export const OPEN_FOLDER_SETTINGS_LABEL = nls.localize('openFolderSettings', "Open Folder Settings");
export class OpenFolderSettingsAction extends Action {
static readonly ID = 'workbench.action.openFolderSettings';
static readonly LABEL = OPEN_FOLDER_SETTINGS_LABEL;
private disposables: IDisposable[] = [];
constructor(
id: string,
label: string,
@IWorkspaceContextService private readonly workspaceContextService: IWorkspaceContextService,
@IPreferencesService private readonly preferencesService: IPreferencesService,
@ICommandService private readonly commandService: ICommandService,
) {
super(id, label);
this.update();
this.workspaceContextService.onDidChangeWorkbenchState(() => this.update(), this, this.disposables);
this.workspaceContextService.onDidChangeWorkspaceFolders(() => this.update(), this, this.disposables);
}
private update(): void {
this.enabled = this.workspaceContextService.getWorkbenchState() === WorkbenchState.WORKSPACE && this.workspaceContextService.getWorkspace().folders.length > 0;
}
run(): Promise<any> {
return this.commandService.executeCommand<IWorkspaceFolder>(PICK_WORKSPACE_FOLDER_COMMAND_ID)
.then(workspaceFolder => {
if (workspaceFolder) {
return this.preferencesService.openFolderSettings(workspaceFolder.uri);
}
return null;
});
}
dispose(): void {
this.disposables = dispose(this.disposables);
super.dispose();
}
}
export class ConfigureLanguageBasedSettingsAction extends Action {
static readonly ID = 'workbench.action.configureLanguageBasedSettings';
static readonly LABEL = nls.localize('configureLanguageBasedSettings', "Configure Language Specific Settings...");
constructor(
id: string,
label: string,
@IModelService private readonly modelService: IModelService,
@IModeService private readonly modeService: IModeService,
@IQuickInputService private readonly quickInputService: IQuickInputService,
@IPreferencesService private readonly preferencesService: IPreferencesService
) {
super(id, label);
}
run(): Promise<any> {
const languages = this.modeService.getRegisteredLanguageNames();
const picks: IQuickPickItem[] = languages.sort().map((lang, index) => {
const description: string = nls.localize('languageDescriptionConfigured', "({0})", this.modeService.getModeIdForLanguageName(lang.toLowerCase()));
// construct a fake resource to be able to show nice icons if any
let fakeResource: URI | undefined;
const extensions = this.modeService.getExtensions(lang);
if (extensions && extensions.length) {
fakeResource = URI.file(extensions[0]);
} else {
const filenames = this.modeService.getFilenames(lang);
if (filenames && filenames.length) {
fakeResource = URI.file(filenames[0]);
}
}
return {
label: lang,
iconClasses: getIconClasses(this.modelService, this.modeService, fakeResource),
description
} as IQuickPickItem;
});
return this.quickInputService.pick(picks, { placeHolder: nls.localize('pickLanguage', "Select Language") })
.then(pick => {
if (pick) {
const modeId = this.modeService.getModeIdForLanguageName(pick.label.toLowerCase());
if (typeof modeId === 'string') {
return this.preferencesService.configureSettingsForLanguage(modeId);
}
}
return undefined;
});
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,999 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { ContextSubMenu } from 'vs/base/browser/contextmenu';
import { getDomNodePagePosition } from 'vs/base/browser/dom';
import { IAction } from 'vs/base/common/actions';
import { Delayer } from 'vs/base/common/async';
import { Emitter, Event } from 'vs/base/common/event';
import { IJSONSchema } from 'vs/base/common/jsonSchema';
import { Disposable, dispose, IDisposable } from 'vs/base/common/lifecycle';
import { ICodeEditor, IEditorMouseEvent, MouseTargetType } from 'vs/editor/browser/editorBrowser';
import { ICursorPositionChangedEvent } from 'vs/editor/common/controller/cursorEvents';
import { Position } from 'vs/editor/common/core/position';
import { IRange, Range } from 'vs/editor/common/core/range';
import * as editorCommon from 'vs/editor/common/editorCommon';
import { IModelDeltaDecoration, TrackedRangeStickiness } from 'vs/editor/common/model';
import { ModelDecorationOptions } from 'vs/editor/common/model/textModel';
import * as nls from 'vs/nls';
import { ConfigurationTarget, IConfigurationService, overrideIdentifierFromKey } from 'vs/platform/configuration/common/configuration';
import { ConfigurationScope, Extensions as ConfigurationExtensions, IConfigurationPropertySchema, IConfigurationRegistry } from 'vs/platform/configuration/common/configurationRegistry';
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { Registry } from 'vs/platform/registry/common/platform';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace';
import { RangeHighlightDecorations } from 'vs/workbench/browser/parts/editor/rangeDecorations';
import { DefaultSettingsHeaderWidget, EditPreferenceWidget, SettingsGroupTitleWidget, SettingsHeaderWidget } from 'vs/workbench/contrib/preferences/browser/preferencesWidgets';
import { IFilterResult, IPreferencesEditorModel, IPreferencesService, ISetting, ISettingsEditorModel, ISettingsGroup } from 'vs/workbench/services/preferences/common/preferences';
import { DefaultSettingsEditorModel, SettingsEditorModel, WorkspaceConfigurationEditorModel } from 'vs/workbench/services/preferences/common/preferencesModels';
export interface IPreferencesRenderer<T> extends IDisposable {
readonly preferencesModel: IPreferencesEditorModel<T>;
getAssociatedPreferencesModel(): IPreferencesEditorModel<T>;
setAssociatedPreferencesModel(associatedPreferencesModel: IPreferencesEditorModel<T>): void;
onFocusPreference: Event<T>;
onClearFocusPreference: Event<T>;
onUpdatePreference: Event<{ key: string, value: any, source: T }>;
render(): void;
updatePreference(key: string, value: any, source: T): void;
focusPreference(setting: T): void;
clearFocus(setting: T): void;
filterPreferences(filterResult: IFilterResult | undefined): void;
editPreference(setting: T): boolean;
}
export class UserSettingsRenderer extends Disposable implements IPreferencesRenderer<ISetting> {
private settingHighlighter: SettingHighlighter;
private editSettingActionRenderer: EditSettingRenderer;
private highlightMatchesRenderer: HighlightMatchesRenderer;
private modelChangeDelayer: Delayer<void> = new Delayer<void>(200);
private associatedPreferencesModel: IPreferencesEditorModel<ISetting>;
private readonly _onFocusPreference = new Emitter<ISetting>();
readonly onFocusPreference: Event<ISetting> = this._onFocusPreference.event;
private readonly _onClearFocusPreference = new Emitter<ISetting>();
readonly onClearFocusPreference: Event<ISetting> = this._onClearFocusPreference.event;
private readonly _onUpdatePreference: Emitter<{ key: string, value: any, source: IIndexedSetting }> = new Emitter<{ key: string, value: any, source: IIndexedSetting }>();
readonly onUpdatePreference: Event<{ key: string, value: any, source: IIndexedSetting }> = this._onUpdatePreference.event;
private filterResult: IFilterResult | undefined;
constructor(protected editor: ICodeEditor, readonly preferencesModel: SettingsEditorModel,
@IPreferencesService protected preferencesService: IPreferencesService,
@IConfigurationService private readonly configurationService: IConfigurationService,
@IInstantiationService protected instantiationService: IInstantiationService
) {
super();
this.settingHighlighter = this._register(instantiationService.createInstance(SettingHighlighter, editor, this._onFocusPreference, this._onClearFocusPreference));
this.highlightMatchesRenderer = this._register(instantiationService.createInstance(HighlightMatchesRenderer, editor));
this.editSettingActionRenderer = this._register(this.instantiationService.createInstance(EditSettingRenderer, this.editor, this.preferencesModel, this.settingHighlighter));
this._register(this.editSettingActionRenderer.onUpdateSetting(({ key, value, source }) => this._updatePreference(key, value, source)));
this._register(this.editor.getModel()!.onDidChangeContent(() => this.modelChangeDelayer.trigger(() => this.onModelChanged())));
}
getAssociatedPreferencesModel(): IPreferencesEditorModel<ISetting> {
return this.associatedPreferencesModel;
}
setAssociatedPreferencesModel(associatedPreferencesModel: IPreferencesEditorModel<ISetting>): void {
this.associatedPreferencesModel = associatedPreferencesModel;
this.editSettingActionRenderer.associatedPreferencesModel = associatedPreferencesModel;
// Create header only in Settings editor mode
this.createHeader();
}
protected createHeader(): void {
this._register(new SettingsHeaderWidget(this.editor, '')).setMessage(nls.localize('emptyUserSettingsHeader', "Place your settings here to override the Default Settings."));
}
render(): void {
this.editSettingActionRenderer.render(this.preferencesModel.settingsGroups, this.associatedPreferencesModel);
if (this.filterResult) {
this.filterPreferences(this.filterResult);
}
}
private _updatePreference(key: string, value: any, source: IIndexedSetting): void {
this._onUpdatePreference.fire({ key, value, source });
this.updatePreference(key, value, source);
}
updatePreference(key: string, value: any, source: IIndexedSetting): void {
const overrideIdentifier = source.overrideOf ? overrideIdentifierFromKey(source.overrideOf.key) : null;
const resource = this.preferencesModel.uri;
this.configurationService.updateValue(key, value, { overrideIdentifier, resource }, this.preferencesModel.configurationTarget)
.then(() => this.onSettingUpdated(source));
}
private onModelChanged(): void {
if (!this.editor.hasModel()) {
// model could have been disposed during the delay
return;
}
this.render();
}
private onSettingUpdated(setting: ISetting) {
this.editor.focus();
setting = this.getSetting(setting)!;
if (setting) {
// TODO:@sandy Selection range should be template range
this.editor.setSelection(setting.valueRange);
this.settingHighlighter.highlight(setting, true);
}
}
private getSetting(setting: ISetting): ISetting | undefined {
const { key, overrideOf } = setting;
if (overrideOf) {
const setting = this.getSetting(overrideOf);
for (const override of setting!.overrides!) {
if (override.key === key) {
return override;
}
}
return undefined;
}
return this.preferencesModel.getPreference(key);
}
filterPreferences(filterResult: IFilterResult | undefined): void {
this.filterResult = filterResult;
this.settingHighlighter.clear(true);
this.highlightMatchesRenderer.render(filterResult ? filterResult.matches : []);
}
focusPreference(setting: ISetting): void {
const s = this.getSetting(setting);
if (s) {
this.settingHighlighter.highlight(s, true);
this.editor.setPosition({ lineNumber: s.keyRange.startLineNumber, column: s.keyRange.startColumn });
} else {
this.settingHighlighter.clear(true);
}
}
clearFocus(setting: ISetting): void {
this.settingHighlighter.clear(true);
}
editPreference(setting: ISetting): boolean {
const editableSetting = this.getSetting(setting);
return !!(editableSetting && this.editSettingActionRenderer.activateOnSetting(editableSetting));
}
}
export class WorkspaceSettingsRenderer extends UserSettingsRenderer implements IPreferencesRenderer<ISetting> {
private workspaceConfigurationRenderer: WorkspaceConfigurationRenderer;
constructor(editor: ICodeEditor, preferencesModel: SettingsEditorModel,
@IPreferencesService preferencesService: IPreferencesService,
@ITelemetryService telemetryService: ITelemetryService,
@IConfigurationService configurationService: IConfigurationService,
@IInstantiationService instantiationService: IInstantiationService
) {
super(editor, preferencesModel, preferencesService, configurationService, instantiationService);
this.workspaceConfigurationRenderer = this._register(instantiationService.createInstance(WorkspaceConfigurationRenderer, editor, preferencesModel));
}
protected createHeader(): void {
this._register(new SettingsHeaderWidget(this.editor, '')).setMessage(nls.localize('emptyWorkspaceSettingsHeader', "Place your settings here to override the User Settings."));
}
setAssociatedPreferencesModel(associatedPreferencesModel: IPreferencesEditorModel<ISetting>): void {
super.setAssociatedPreferencesModel(associatedPreferencesModel);
this.workspaceConfigurationRenderer.render(this.getAssociatedPreferencesModel());
}
render(): void {
super.render();
this.workspaceConfigurationRenderer.render(this.getAssociatedPreferencesModel());
}
}
export class FolderSettingsRenderer extends UserSettingsRenderer implements IPreferencesRenderer<ISetting> {
constructor(editor: ICodeEditor, preferencesModel: SettingsEditorModel,
@IPreferencesService preferencesService: IPreferencesService,
@ITelemetryService telemetryService: ITelemetryService,
@IConfigurationService configurationService: IConfigurationService,
@IInstantiationService instantiationService: IInstantiationService
) {
super(editor, preferencesModel, preferencesService, configurationService, instantiationService);
}
protected createHeader(): void {
this._register(new SettingsHeaderWidget(this.editor, '')).setMessage(nls.localize('emptyFolderSettingsHeader', "Place your folder settings here to override those from the Workspace Settings."));
}
}
export class DefaultSettingsRenderer extends Disposable implements IPreferencesRenderer<ISetting> {
private _associatedPreferencesModel: IPreferencesEditorModel<ISetting>;
private settingHighlighter: SettingHighlighter;
private settingsHeaderRenderer: DefaultSettingsHeaderRenderer;
private settingsGroupTitleRenderer: SettingsGroupTitleRenderer;
private filteredMatchesRenderer: FilteredMatchesRenderer;
private hiddenAreasRenderer: HiddenAreasRenderer;
private editSettingActionRenderer: EditSettingRenderer;
private bracesHidingRenderer: BracesHidingRenderer;
private filterResult: IFilterResult | undefined;
private readonly _onUpdatePreference: Emitter<{ key: string, value: any, source: IIndexedSetting }> = new Emitter<{ key: string, value: any, source: IIndexedSetting }>();
readonly onUpdatePreference: Event<{ key: string, value: any, source: IIndexedSetting }> = this._onUpdatePreference.event;
private readonly _onFocusPreference = new Emitter<ISetting>();
readonly onFocusPreference: Event<ISetting> = this._onFocusPreference.event;
private readonly _onClearFocusPreference = new Emitter<ISetting>();
readonly onClearFocusPreference: Event<ISetting> = this._onClearFocusPreference.event;
constructor(protected editor: ICodeEditor, readonly preferencesModel: DefaultSettingsEditorModel,
@IPreferencesService protected preferencesService: IPreferencesService,
@IInstantiationService protected instantiationService: IInstantiationService,
) {
super();
this.settingHighlighter = this._register(instantiationService.createInstance(SettingHighlighter, editor, this._onFocusPreference, this._onClearFocusPreference));
this.settingsHeaderRenderer = this._register(instantiationService.createInstance(DefaultSettingsHeaderRenderer, editor));
this.settingsGroupTitleRenderer = this._register(instantiationService.createInstance(SettingsGroupTitleRenderer, editor));
this.filteredMatchesRenderer = this._register(instantiationService.createInstance(FilteredMatchesRenderer, editor));
this.editSettingActionRenderer = this._register(instantiationService.createInstance(EditSettingRenderer, editor, preferencesModel, this.settingHighlighter));
this.bracesHidingRenderer = this._register(instantiationService.createInstance(BracesHidingRenderer, editor, preferencesModel));
this.hiddenAreasRenderer = this._register(instantiationService.createInstance(HiddenAreasRenderer, editor, [this.settingsGroupTitleRenderer, this.filteredMatchesRenderer, this.bracesHidingRenderer]));
this._register(this.editSettingActionRenderer.onUpdateSetting(e => this._onUpdatePreference.fire(e)));
this._register(this.settingsGroupTitleRenderer.onHiddenAreasChanged(() => this.hiddenAreasRenderer.render()));
this._register(preferencesModel.onDidChangeGroups(() => this.render()));
}
getAssociatedPreferencesModel(): IPreferencesEditorModel<ISetting> {
return this._associatedPreferencesModel;
}
setAssociatedPreferencesModel(associatedPreferencesModel: IPreferencesEditorModel<ISetting>): void {
this._associatedPreferencesModel = associatedPreferencesModel;
this.editSettingActionRenderer.associatedPreferencesModel = associatedPreferencesModel;
}
render() {
this.settingsGroupTitleRenderer.render(this.preferencesModel.settingsGroups);
this.editSettingActionRenderer.render(this.preferencesModel.settingsGroups, this._associatedPreferencesModel);
this.settingHighlighter.clear(true);
this.bracesHidingRenderer.render(undefined, this.preferencesModel.settingsGroups);
this.settingsGroupTitleRenderer.showGroup(0);
this.hiddenAreasRenderer.render();
}
filterPreferences(filterResult: IFilterResult | undefined): void {
this.filterResult = filterResult;
if (filterResult) {
this.filteredMatchesRenderer.render(filterResult, this.preferencesModel.settingsGroups);
this.settingsGroupTitleRenderer.render(undefined);
this.settingsHeaderRenderer.render(filterResult);
this.settingHighlighter.clear(true);
this.bracesHidingRenderer.render(filterResult, this.preferencesModel.settingsGroups);
this.editSettingActionRenderer.render(filterResult.filteredGroups, this._associatedPreferencesModel);
} else {
this.settingHighlighter.clear(true);
this.filteredMatchesRenderer.render(undefined, this.preferencesModel.settingsGroups);
this.settingsHeaderRenderer.render(undefined);
this.settingsGroupTitleRenderer.render(this.preferencesModel.settingsGroups);
this.settingsGroupTitleRenderer.showGroup(0);
this.bracesHidingRenderer.render(undefined, this.preferencesModel.settingsGroups);
this.editSettingActionRenderer.render(this.preferencesModel.settingsGroups, this._associatedPreferencesModel);
}
this.hiddenAreasRenderer.render();
}
focusPreference(s: ISetting): void {
const setting = this.getSetting(s);
if (setting) {
this.settingsGroupTitleRenderer.showSetting(setting);
this.settingHighlighter.highlight(setting, true);
} else {
this.settingHighlighter.clear(true);
}
}
private getSetting(setting: ISetting): ISetting | undefined {
const { key, overrideOf } = setting;
if (overrideOf) {
const setting = this.getSetting(overrideOf);
for (const override of setting!.overrides!) {
if (override.key === key) {
return override;
}
}
return undefined;
}
const settingsGroups = this.filterResult ? this.filterResult.filteredGroups : this.preferencesModel.settingsGroups;
return this.getPreference(key, settingsGroups);
}
private getPreference(key: string, settingsGroups: ISettingsGroup[]): ISetting | undefined {
for (const group of settingsGroups) {
for (const section of group.sections) {
for (const setting of section.settings) {
if (setting.key === key) {
return setting;
}
}
}
}
return undefined;
}
clearFocus(setting: ISetting): void {
this.settingHighlighter.clear(true);
}
updatePreference(key: string, value: any, source: ISetting): void {
}
editPreference(setting: ISetting): boolean {
return this.editSettingActionRenderer.activateOnSetting(setting);
}
}
export interface HiddenAreasProvider {
hiddenAreas: IRange[];
}
export class BracesHidingRenderer extends Disposable implements HiddenAreasProvider {
private _result: IFilterResult | undefined;
private _settingsGroups: ISettingsGroup[];
constructor(private editor: ICodeEditor) {
super();
}
render(result: IFilterResult | undefined, settingsGroups: ISettingsGroup[]): void {
this._result = result;
this._settingsGroups = settingsGroups;
}
get hiddenAreas(): IRange[] {
// Opening square brace
const hiddenAreas = [
{
startLineNumber: 1,
startColumn: 1,
endLineNumber: 2,
endColumn: 1
}
];
const hideBraces = (group: ISettingsGroup, hideExtraLine?: boolean) => {
// Opening curly brace
hiddenAreas.push({
startLineNumber: group.range.startLineNumber - 3,
startColumn: 1,
endLineNumber: group.range.startLineNumber - (hideExtraLine ? 1 : 3),
endColumn: 1
});
// Closing curly brace
hiddenAreas.push({
startLineNumber: group.range.endLineNumber + 1,
startColumn: 1,
endLineNumber: group.range.endLineNumber + 4,
endColumn: 1
});
};
this._settingsGroups.forEach(g => hideBraces(g));
if (this._result) {
this._result.filteredGroups.forEach((g, i) => hideBraces(g, true));
}
// Closing square brace
const lineCount = this.editor.getModel()!.getLineCount();
hiddenAreas.push({
startLineNumber: lineCount,
startColumn: 1,
endLineNumber: lineCount,
endColumn: 1
});
return hiddenAreas;
}
}
class DefaultSettingsHeaderRenderer extends Disposable {
private settingsHeaderWidget: DefaultSettingsHeaderWidget;
readonly onClick: Event<void>;
constructor(editor: ICodeEditor) {
super();
this.settingsHeaderWidget = this._register(new DefaultSettingsHeaderWidget(editor, ''));
this.onClick = this.settingsHeaderWidget.onClick;
}
render(filterResult: IFilterResult | undefined) {
const hasSettings = !filterResult || filterResult.filteredGroups.length > 0;
this.settingsHeaderWidget.toggleMessage(hasSettings);
}
}
export class SettingsGroupTitleRenderer extends Disposable implements HiddenAreasProvider {
private readonly _onHiddenAreasChanged = new Emitter<void>();
get onHiddenAreasChanged(): Event<void> { return this._onHiddenAreasChanged.event; }
private settingsGroups: ISettingsGroup[];
private hiddenGroups: ISettingsGroup[] = [];
private settingsGroupTitleWidgets: SettingsGroupTitleWidget[];
private disposables: IDisposable[] = [];
constructor(private editor: ICodeEditor,
@IInstantiationService private readonly instantiationService: IInstantiationService
) {
super();
}
get hiddenAreas(): IRange[] {
const hiddenAreas: IRange[] = [];
for (const group of this.hiddenGroups) {
hiddenAreas.push(group.range);
}
return hiddenAreas;
}
render(settingsGroups: ISettingsGroup[] | undefined) {
this.disposeWidgets();
if (!settingsGroups) {
return;
}
this.settingsGroups = settingsGroups.slice();
this.settingsGroupTitleWidgets = [];
for (const group of this.settingsGroups.slice().reverse()) {
if (group.sections.every(sect => sect.settings.length === 0)) {
continue;
}
const settingsGroupTitleWidget = this.instantiationService.createInstance(SettingsGroupTitleWidget, this.editor, group);
settingsGroupTitleWidget.render();
this.settingsGroupTitleWidgets.push(settingsGroupTitleWidget);
this.disposables.push(settingsGroupTitleWidget);
this.disposables.push(settingsGroupTitleWidget.onToggled(collapsed => this.onToggled(collapsed, settingsGroupTitleWidget.settingsGroup)));
}
this.settingsGroupTitleWidgets.reverse();
}
showGroup(groupIdx: number) {
const shownGroup = this.settingsGroupTitleWidgets[groupIdx].settingsGroup;
this.hiddenGroups = this.settingsGroups.filter(g => g !== shownGroup);
for (const groupTitleWidget of this.settingsGroupTitleWidgets.filter(widget => widget.settingsGroup !== shownGroup)) {
groupTitleWidget.toggleCollapse(true);
}
this._onHiddenAreasChanged.fire();
}
showSetting(setting: ISetting): void {
const settingsGroupTitleWidget = this.settingsGroupTitleWidgets.filter(widget => Range.containsRange(widget.settingsGroup.range, setting.range))[0];
if (settingsGroupTitleWidget && settingsGroupTitleWidget.isCollapsed()) {
settingsGroupTitleWidget.toggleCollapse(false);
this.hiddenGroups.splice(this.hiddenGroups.indexOf(settingsGroupTitleWidget.settingsGroup), 1);
this._onHiddenAreasChanged.fire();
}
}
private onToggled(collapsed: boolean, group: ISettingsGroup) {
const index = this.hiddenGroups.indexOf(group);
if (collapsed) {
const currentPosition = this.editor.getPosition();
if (group.range.startLineNumber <= currentPosition!.lineNumber && group.range.endLineNumber >= currentPosition!.lineNumber) {
this.editor.setPosition({ lineNumber: group.range.startLineNumber - 1, column: 1 });
}
this.hiddenGroups.push(group);
} else {
this.hiddenGroups.splice(index, 1);
}
this._onHiddenAreasChanged.fire();
}
private disposeWidgets() {
this.hiddenGroups = [];
this.disposables = dispose(this.disposables);
}
dispose() {
this.disposeWidgets();
super.dispose();
}
}
export class HiddenAreasRenderer extends Disposable {
constructor(private editor: ICodeEditor, private hiddenAreasProviders: HiddenAreasProvider[]
) {
super();
}
render() {
const ranges: IRange[] = [];
for (const hiddenAreaProvider of this.hiddenAreasProviders) {
ranges.push(...hiddenAreaProvider.hiddenAreas);
}
this.editor.setHiddenAreas(ranges);
}
dispose() {
this.editor.setHiddenAreas([]);
super.dispose();
}
}
export class FilteredMatchesRenderer extends Disposable implements HiddenAreasProvider {
private decorationIds: string[] = [];
hiddenAreas: IRange[] = [];
constructor(private editor: ICodeEditor
) {
super();
}
render(result: IFilterResult | undefined, allSettingsGroups: ISettingsGroup[]): void {
this.hiddenAreas = [];
if (result) {
this.hiddenAreas = this.computeHiddenRanges(result.filteredGroups, result.allGroups);
this.decorationIds = this.editor.deltaDecorations(this.decorationIds, result.matches.map(match => this.createDecoration(match)));
} else {
this.hiddenAreas = this.computeHiddenRanges(undefined, allSettingsGroups);
this.decorationIds = this.editor.deltaDecorations(this.decorationIds, []);
}
}
private createDecoration(range: IRange): IModelDeltaDecoration {
return {
range,
options: FilteredMatchesRenderer._FIND_MATCH
};
}
private static readonly _FIND_MATCH = ModelDecorationOptions.register({
stickiness: TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges,
className: 'findMatch'
});
private computeHiddenRanges(filteredGroups: ISettingsGroup[] | undefined, allSettingsGroups: ISettingsGroup[]): IRange[] {
// Hide the contents of hidden groups
const notMatchesRanges: IRange[] = [];
if (filteredGroups) {
allSettingsGroups.forEach((group, i) => {
notMatchesRanges.push({
startLineNumber: group.range.startLineNumber - 1,
startColumn: group.range.startColumn,
endLineNumber: group.range.endLineNumber,
endColumn: group.range.endColumn
});
});
}
return notMatchesRanges;
}
dispose() {
this.decorationIds = this.editor.deltaDecorations(this.decorationIds, []);
super.dispose();
}
}
export class HighlightMatchesRenderer extends Disposable {
private decorationIds: string[] = [];
constructor(private editor: ICodeEditor
) {
super();
}
render(matches: IRange[]): void {
this.decorationIds = this.editor.deltaDecorations(this.decorationIds, matches.map(match => this.createDecoration(match)));
}
private static readonly _FIND_MATCH = ModelDecorationOptions.register({
stickiness: TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges,
className: 'findMatch'
});
private createDecoration(range: IRange): IModelDeltaDecoration {
return {
range,
options: HighlightMatchesRenderer._FIND_MATCH
};
}
dispose() {
this.decorationIds = this.editor.deltaDecorations(this.decorationIds, []);
super.dispose();
}
}
export interface IIndexedSetting extends ISetting {
index: number;
groupId: string;
}
class EditSettingRenderer extends Disposable {
private editPreferenceWidgetForCursorPosition: EditPreferenceWidget<IIndexedSetting>;
private editPreferenceWidgetForMouseMove: EditPreferenceWidget<IIndexedSetting>;
private settingsGroups: ISettingsGroup[] = [];
associatedPreferencesModel: IPreferencesEditorModel<ISetting>;
private toggleEditPreferencesForMouseMoveDelayer: Delayer<void>;
private readonly _onUpdateSetting: Emitter<{ key: string, value: any, source: IIndexedSetting }> = new Emitter<{ key: string, value: any, source: IIndexedSetting }>();
readonly onUpdateSetting: Event<{ key: string, value: any, source: IIndexedSetting }> = this._onUpdateSetting.event;
constructor(private editor: ICodeEditor, private masterSettingsModel: ISettingsEditorModel,
private settingHighlighter: SettingHighlighter,
@IInstantiationService private readonly instantiationService: IInstantiationService,
@IContextMenuService private readonly contextMenuService: IContextMenuService
) {
super();
this.editPreferenceWidgetForCursorPosition = <EditPreferenceWidget<IIndexedSetting>>this._register(this.instantiationService.createInstance(EditPreferenceWidget, editor));
this.editPreferenceWidgetForMouseMove = <EditPreferenceWidget<IIndexedSetting>>this._register(this.instantiationService.createInstance(EditPreferenceWidget, editor));
this.toggleEditPreferencesForMouseMoveDelayer = new Delayer<void>(75);
this._register(this.editPreferenceWidgetForCursorPosition.onClick(e => this.onEditSettingClicked(this.editPreferenceWidgetForCursorPosition, e)));
this._register(this.editPreferenceWidgetForMouseMove.onClick(e => this.onEditSettingClicked(this.editPreferenceWidgetForMouseMove, e)));
this._register(this.editor.onDidChangeCursorPosition(positionChangeEvent => this.onPositionChanged(positionChangeEvent)));
this._register(this.editor.onMouseMove(mouseMoveEvent => this.onMouseMoved(mouseMoveEvent)));
this._register(this.editor.onDidChangeConfiguration(() => this.onConfigurationChanged()));
}
render(settingsGroups: ISettingsGroup[], associatedPreferencesModel: IPreferencesEditorModel<ISetting>): void {
this.editPreferenceWidgetForCursorPosition.hide();
this.editPreferenceWidgetForMouseMove.hide();
this.settingsGroups = settingsGroups;
this.associatedPreferencesModel = associatedPreferencesModel;
const settings = this.getSettings(this.editor.getPosition()!.lineNumber);
if (settings.length) {
this.showEditPreferencesWidget(this.editPreferenceWidgetForCursorPosition, settings);
}
}
private isDefaultSettings(): boolean {
return this.masterSettingsModel instanceof DefaultSettingsEditorModel;
}
private onConfigurationChanged(): void {
if (!this.editor.getConfiguration().viewInfo.glyphMargin) {
this.editPreferenceWidgetForCursorPosition.hide();
this.editPreferenceWidgetForMouseMove.hide();
}
}
private onPositionChanged(positionChangeEvent: ICursorPositionChangedEvent) {
this.editPreferenceWidgetForMouseMove.hide();
const settings = this.getSettings(positionChangeEvent.position.lineNumber);
if (settings.length) {
this.showEditPreferencesWidget(this.editPreferenceWidgetForCursorPosition, settings);
} else {
this.editPreferenceWidgetForCursorPosition.hide();
}
}
private onMouseMoved(mouseMoveEvent: IEditorMouseEvent): void {
const editPreferenceWidget = this.getEditPreferenceWidgetUnderMouse(mouseMoveEvent);
if (editPreferenceWidget) {
this.onMouseOver(editPreferenceWidget);
return;
}
this.settingHighlighter.clear();
this.toggleEditPreferencesForMouseMoveDelayer.trigger(() => this.toggleEditPreferenceWidgetForMouseMove(mouseMoveEvent));
}
private getEditPreferenceWidgetUnderMouse(mouseMoveEvent: IEditorMouseEvent): EditPreferenceWidget<ISetting> | undefined {
if (mouseMoveEvent.target.type === MouseTargetType.GUTTER_GLYPH_MARGIN) {
const line = mouseMoveEvent.target.position!.lineNumber;
if (this.editPreferenceWidgetForMouseMove.getLine() === line && this.editPreferenceWidgetForMouseMove.isVisible()) {
return this.editPreferenceWidgetForMouseMove;
}
if (this.editPreferenceWidgetForCursorPosition.getLine() === line && this.editPreferenceWidgetForCursorPosition.isVisible()) {
return this.editPreferenceWidgetForCursorPosition;
}
}
return undefined;
}
private toggleEditPreferenceWidgetForMouseMove(mouseMoveEvent: IEditorMouseEvent): void {
const settings = mouseMoveEvent.target.position ? this.getSettings(mouseMoveEvent.target.position.lineNumber) : null;
if (settings && settings.length) {
this.showEditPreferencesWidget(this.editPreferenceWidgetForMouseMove, settings);
} else {
this.editPreferenceWidgetForMouseMove.hide();
}
}
private showEditPreferencesWidget(editPreferencesWidget: EditPreferenceWidget<ISetting>, settings: IIndexedSetting[]) {
const line = settings[0].valueRange.startLineNumber;
if (this.editor.getConfiguration().viewInfo.glyphMargin && this.marginFreeFromOtherDecorations(line)) {
editPreferencesWidget.show(line, nls.localize('editTtile', "Edit"), settings);
const editPreferenceWidgetToHide = editPreferencesWidget === this.editPreferenceWidgetForCursorPosition ? this.editPreferenceWidgetForMouseMove : this.editPreferenceWidgetForCursorPosition;
editPreferenceWidgetToHide.hide();
}
}
private marginFreeFromOtherDecorations(line: number): boolean {
const decorations = this.editor.getLineDecorations(line);
if (decorations) {
for (const { options } of decorations) {
if (options.glyphMarginClassName && options.glyphMarginClassName.indexOf(EditPreferenceWidget.GLYPH_MARGIN_CLASS_NAME) === -1) {
return false;
}
}
}
return true;
}
private getSettings(lineNumber: number): IIndexedSetting[] {
const configurationMap = this.getConfigurationsMap();
return this.getSettingsAtLineNumber(lineNumber).filter(setting => {
const configurationNode = configurationMap[setting.key];
if (configurationNode) {
if (this.isDefaultSettings()) {
if (setting.key === 'launch') {
// Do not show because of https://github.com/Microsoft/vscode/issues/32593
return false;
}
return true;
}
if (configurationNode.type === 'boolean' || configurationNode.enum) {
if ((<SettingsEditorModel>this.masterSettingsModel).configurationTarget !== ConfigurationTarget.WORKSPACE_FOLDER) {
return true;
}
if (configurationNode.scope === ConfigurationScope.RESOURCE) {
return true;
}
}
}
return false;
});
}
private getSettingsAtLineNumber(lineNumber: number): IIndexedSetting[] {
// index of setting, across all groups/sections
let index = 0;
const settings: IIndexedSetting[] = [];
for (const group of this.settingsGroups) {
if (group.range.startLineNumber > lineNumber) {
break;
}
if (lineNumber >= group.range.startLineNumber && lineNumber <= group.range.endLineNumber) {
for (const section of group.sections) {
for (const setting of section.settings) {
if (setting.range.startLineNumber > lineNumber) {
break;
}
if (lineNumber >= setting.range.startLineNumber && lineNumber <= setting.range.endLineNumber) {
if (!this.isDefaultSettings() && setting.overrides!.length) {
// Only one level because override settings cannot have override settings
for (const overrideSetting of setting.overrides!) {
if (lineNumber >= overrideSetting.range.startLineNumber && lineNumber <= overrideSetting.range.endLineNumber) {
settings.push({ ...overrideSetting, index, groupId: group.id });
}
}
} else {
settings.push({ ...setting, index, groupId: group.id });
}
}
index++;
}
}
}
}
return settings;
}
private onMouseOver(editPreferenceWidget: EditPreferenceWidget<ISetting>): void {
this.settingHighlighter.highlight(editPreferenceWidget.preferences[0]);
}
private onEditSettingClicked(editPreferenceWidget: EditPreferenceWidget<IIndexedSetting>, e: IEditorMouseEvent): void {
const anchor = { x: e.event.posx, y: e.event.posy + 10 };
const actions = this.getSettings(editPreferenceWidget.getLine()).length === 1 ? this.getActions(editPreferenceWidget.preferences[0], this.getConfigurationsMap()[editPreferenceWidget.preferences[0].key])
: editPreferenceWidget.preferences.map(setting => new ContextSubMenu(setting.key, this.getActions(setting, this.getConfigurationsMap()[setting.key])));
this.contextMenuService.showContextMenu({
getAnchor: () => anchor,
getActions: () => actions
});
}
activateOnSetting(setting: ISetting): boolean {
const startLine = setting.keyRange.startLineNumber;
const settings = this.getSettings(startLine);
if (!settings.length) {
return false;
}
this.editPreferenceWidgetForMouseMove.show(startLine, '', settings);
const actions = this.getActions(this.editPreferenceWidgetForMouseMove.preferences[0], this.getConfigurationsMap()[this.editPreferenceWidgetForMouseMove.preferences[0].key]);
this.contextMenuService.showContextMenu({
getAnchor: () => this.toAbsoluteCoords(new Position(startLine, 1)),
getActions: () => actions
});
return true;
}
private toAbsoluteCoords(position: Position): { x: number, y: number } {
const positionCoords = this.editor.getScrolledVisiblePosition(position);
const editorCoords = getDomNodePagePosition(this.editor.getDomNode()!);
const x = editorCoords.left + positionCoords!.left;
const y = editorCoords.top + positionCoords!.top + positionCoords!.height;
return { x, y: y + 10 };
}
private getConfigurationsMap(): { [qualifiedKey: string]: IConfigurationPropertySchema } {
return Registry.as<IConfigurationRegistry>(ConfigurationExtensions.Configuration).getConfigurationProperties();
}
private getActions(setting: IIndexedSetting, jsonSchema: IJSONSchema): IAction[] {
if (jsonSchema.type === 'boolean') {
return [<IAction>{
id: 'truthyValue',
label: 'true',
enabled: true,
run: () => this.updateSetting(setting.key, true, setting)
}, <IAction>{
id: 'falsyValue',
label: 'false',
enabled: true,
run: () => this.updateSetting(setting.key, false, setting)
}];
}
if (jsonSchema.enum) {
return jsonSchema.enum.map(value => {
return <IAction>{
id: value,
label: JSON.stringify(value),
enabled: true,
run: () => this.updateSetting(setting.key, value, setting)
};
});
}
return this.getDefaultActions(setting);
}
private getDefaultActions(setting: IIndexedSetting): IAction[] {
if (this.isDefaultSettings()) {
const settingInOtherModel = this.associatedPreferencesModel.getPreference(setting.key);
return [<IAction>{
id: 'setDefaultValue',
label: settingInOtherModel ? nls.localize('replaceDefaultValue', "Replace in Settings") : nls.localize('copyDefaultValue', "Copy to Settings"),
enabled: true,
run: () => this.updateSetting(setting.key, setting.value, setting)
}];
}
return [];
}
private updateSetting(key: string, value: any, source: IIndexedSetting): void {
this._onUpdateSetting.fire({ key, value, source });
}
}
class SettingHighlighter extends Disposable {
private fixedHighlighter: RangeHighlightDecorations;
private volatileHighlighter: RangeHighlightDecorations;
private highlightedSetting: ISetting;
constructor(private editor: ICodeEditor, private readonly focusEventEmitter: Emitter<ISetting>, private readonly clearFocusEventEmitter: Emitter<ISetting>,
@IInstantiationService instantiationService: IInstantiationService
) {
super();
this.fixedHighlighter = this._register(instantiationService.createInstance(RangeHighlightDecorations));
this.volatileHighlighter = this._register(instantiationService.createInstance(RangeHighlightDecorations));
this.fixedHighlighter.onHighlghtRemoved(() => this.clearFocusEventEmitter.fire(this.highlightedSetting));
this.volatileHighlighter.onHighlghtRemoved(() => this.clearFocusEventEmitter.fire(this.highlightedSetting));
}
highlight(setting: ISetting, fix: boolean = false) {
this.highlightedSetting = setting;
this.volatileHighlighter.removeHighlightRange();
this.fixedHighlighter.removeHighlightRange();
const highlighter = fix ? this.fixedHighlighter : this.volatileHighlighter;
highlighter.highlightRange({
range: setting.valueRange,
resource: this.editor.getModel()!.uri
}, this.editor);
this.editor.revealLineInCenterIfOutsideViewport(setting.valueRange.startLineNumber, editorCommon.ScrollType.Smooth);
this.focusEventEmitter.fire(setting);
}
clear(fix: boolean = false): void {
this.volatileHighlighter.removeHighlightRange();
if (fix) {
this.fixedHighlighter.removeHighlightRange();
}
this.clearFocusEventEmitter.fire(this.highlightedSetting);
}
}
class WorkspaceConfigurationRenderer extends Disposable {
private decorationIds: string[] = [];
private associatedSettingsEditorModel: IPreferencesEditorModel<ISetting>;
private renderingDelayer: Delayer<void> = new Delayer<void>(200);
constructor(private editor: ICodeEditor, private workspaceSettingsEditorModel: SettingsEditorModel,
@IWorkspaceContextService private readonly workspaceContextService: IWorkspaceContextService
) {
super();
this._register(this.editor.getModel()!.onDidChangeContent(() => this.renderingDelayer.trigger(() => this.render(this.associatedSettingsEditorModel))));
}
render(associatedSettingsEditorModel: IPreferencesEditorModel<ISetting>): void {
this.associatedSettingsEditorModel = associatedSettingsEditorModel;
// Dim other configurations in workspace configuration file only in the context of Settings Editor
if (this.associatedSettingsEditorModel && this.workspaceContextService.getWorkbenchState() === WorkbenchState.WORKSPACE && this.workspaceSettingsEditorModel instanceof WorkspaceConfigurationEditorModel) {
const ranges: IRange[] = [];
for (const settingsGroup of this.workspaceSettingsEditorModel.configurationGroups) {
for (const section of settingsGroup.sections) {
for (const setting of section.settings) {
if (setting.key !== 'settings') {
ranges.push({
startLineNumber: setting.keyRange.startLineNumber,
startColumn: setting.keyRange.startColumn - 1,
endLineNumber: setting.valueRange.endLineNumber,
endColumn: setting.valueRange.endColumn
});
}
}
}
}
this.decorationIds = this.editor.deltaDecorations(this.decorationIds, ranges.map(range => this.createDecoration(range)));
}
}
private static readonly _DIM_CONFIGURATION_ = ModelDecorationOptions.register({
stickiness: TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges,
inlineClassName: 'dim-configuration'
});
private createDecoration(range: IRange): IModelDeltaDecoration {
return {
range,
options: WorkspaceConfigurationRenderer._DIM_CONFIGURATION_
};
}
dispose(): void {
this.decorationIds = this.editor.deltaDecorations(this.decorationIds, []);
super.dispose();
}
}

View File

@@ -0,0 +1,852 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as DOM from 'vs/base/browser/dom';
import { IKeyboardEvent, StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent';
import { ActionBar, ActionsOrientation, BaseActionItem } from 'vs/base/browser/ui/actionbar/actionbar';
import { IInputOptions, InputBox } from 'vs/base/browser/ui/inputbox/inputBox';
import { Widget } from 'vs/base/browser/ui/widget';
import { Action, IAction } from 'vs/base/common/actions';
import { Emitter, Event } from 'vs/base/common/event';
import { MarkdownString } from 'vs/base/common/htmlContent';
import { KeyCode } from 'vs/base/common/keyCodes';
import { Disposable, dispose, IDisposable } from 'vs/base/common/lifecycle';
import { URI } from 'vs/base/common/uri';
import { IMarginData } from 'vs/editor/browser/controller/mouseTarget';
import { ICodeEditor, IEditorMouseEvent, IViewZone, MouseTargetType } from 'vs/editor/browser/editorBrowser';
import { ICursorPositionChangedEvent } from 'vs/editor/common/controller/cursorEvents';
import { Position } from 'vs/editor/common/core/position';
import { IModelDeltaDecoration, TrackedRangeStickiness } from 'vs/editor/common/model';
import { localize } from 'vs/nls';
import { ConfigurationTarget } from 'vs/platform/configuration/common/configuration';
import { IContextKey } from 'vs/platform/contextkey/common/contextkey';
import { IContextMenuService, IContextViewService } from 'vs/platform/contextview/browser/contextView';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { activeContrastBorder, badgeBackground, badgeForeground, contrastBorder, focusBorder } from 'vs/platform/theme/common/colorRegistry';
import { attachInputBoxStyler, attachStylerCallback } from 'vs/platform/theme/common/styler';
import { ICssStyleCollector, ITheme, IThemeService, registerThemingParticipant } from 'vs/platform/theme/common/themeService';
import { IWorkspaceContextService, IWorkspaceFolder, WorkbenchState } from 'vs/platform/workspace/common/workspace';
import { PANEL_ACTIVE_TITLE_BORDER, PANEL_ACTIVE_TITLE_FOREGROUND, PANEL_INACTIVE_TITLE_FOREGROUND } from 'vs/workbench/common/theme';
import { ISettingsGroup } from 'vs/workbench/services/preferences/common/preferences';
export class SettingsHeaderWidget extends Widget implements IViewZone {
private id: number;
private _domNode: HTMLElement;
protected titleContainer: HTMLElement;
private messageElement: HTMLElement;
constructor(protected editor: ICodeEditor, private title: string) {
super();
this.create();
this._register(this.editor.onDidChangeConfiguration(() => this.layout()));
this._register(this.editor.onDidLayoutChange(() => this.layout()));
}
get domNode(): HTMLElement {
return this._domNode;
}
get heightInLines(): number {
return 1;
}
get afterLineNumber(): number {
return 0;
}
protected create() {
this._domNode = DOM.$('.settings-header-widget');
this.titleContainer = DOM.append(this._domNode, DOM.$('.title-container'));
if (this.title) {
DOM.append(this.titleContainer, DOM.$('.title')).textContent = this.title;
}
this.messageElement = DOM.append(this.titleContainer, DOM.$('.message'));
if (this.title) {
this.messageElement.style.paddingLeft = '12px';
}
this.editor.changeViewZones(accessor => {
this.id = accessor.addZone(this);
this.layout();
});
}
setMessage(message: string): void {
this.messageElement.textContent = message;
}
private layout(): void {
const configuration = this.editor.getConfiguration();
this.titleContainer.style.fontSize = configuration.fontInfo.fontSize + 'px';
if (!configuration.contribInfo.folding) {
this.titleContainer.style.paddingLeft = '6px';
}
}
dispose() {
this.editor.changeViewZones(accessor => {
accessor.removeZone(this.id);
});
super.dispose();
}
}
export class DefaultSettingsHeaderWidget extends SettingsHeaderWidget {
private _onClick = this._register(new Emitter<void>());
readonly onClick: Event<void> = this._onClick.event;
protected create() {
super.create();
this.toggleMessage(true);
}
toggleMessage(hasSettings: boolean): void {
if (hasSettings) {
this.setMessage(localize('defaultSettings', "Place your settings in the right hand side editor to override."));
} else {
this.setMessage(localize('noSettingsFound', "No Settings Found."));
}
}
}
export class SettingsGroupTitleWidget extends Widget implements IViewZone {
private id: number;
private _afterLineNumber: number;
private _domNode: HTMLElement;
private titleContainer: HTMLElement;
private icon: HTMLElement;
private title: HTMLElement;
private _onToggled = this._register(new Emitter<boolean>());
readonly onToggled: Event<boolean> = this._onToggled.event;
private previousPosition: Position;
constructor(private editor: ICodeEditor, public settingsGroup: ISettingsGroup) {
super();
this.create();
this._register(this.editor.onDidChangeConfiguration(() => this.layout()));
this._register(this.editor.onDidLayoutChange(() => this.layout()));
this._register(this.editor.onDidChangeCursorPosition((e) => this.onCursorChange(e)));
}
get domNode(): HTMLElement {
return this._domNode;
}
get heightInLines(): number {
return 1.5;
}
get afterLineNumber(): number {
return this._afterLineNumber;
}
private create() {
this._domNode = DOM.$('.settings-group-title-widget');
this.titleContainer = DOM.append(this._domNode, DOM.$('.title-container'));
this.titleContainer.tabIndex = 0;
this.onclick(this.titleContainer, () => this.toggle());
this.onkeydown(this.titleContainer, (e) => this.onKeyDown(e));
const focusTracker = this._register(DOM.trackFocus(this.titleContainer));
this._register(focusTracker.onDidFocus(() => this.toggleFocus(true)));
this._register(focusTracker.onDidBlur(() => this.toggleFocus(false)));
this.icon = DOM.append(this.titleContainer, DOM.$('.expand-collapse-icon'));
this.title = DOM.append(this.titleContainer, DOM.$('.title'));
this.title.textContent = this.settingsGroup.title + ` (${this.settingsGroup.sections.reduce((count, section) => count + section.settings.length, 0)})`;
this.layout();
}
render() {
if (!this.settingsGroup.range) {
// #61352
return;
}
this._afterLineNumber = this.settingsGroup.range.startLineNumber - 2;
this.editor.changeViewZones(accessor => {
this.id = accessor.addZone(this);
this.layout();
});
}
toggleCollapse(collapse: boolean) {
DOM.toggleClass(this.titleContainer, 'collapsed', collapse);
}
toggleFocus(focus: boolean): void {
DOM.toggleClass(this.titleContainer, 'focused', focus);
}
isCollapsed(): boolean {
return DOM.hasClass(this.titleContainer, 'collapsed');
}
private layout(): void {
const configuration = this.editor.getConfiguration();
const layoutInfo = this.editor.getLayoutInfo();
this._domNode.style.width = layoutInfo.contentWidth - layoutInfo.verticalScrollbarWidth + 'px';
this.titleContainer.style.lineHeight = configuration.lineHeight + 3 + 'px';
this.titleContainer.style.height = configuration.lineHeight + 3 + 'px';
this.titleContainer.style.fontSize = configuration.fontInfo.fontSize + 'px';
this.icon.style.minWidth = `${this.getIconSize(16)}px`;
}
private getIconSize(minSize: number): number {
const fontSize = this.editor.getConfiguration().fontInfo.fontSize;
return fontSize > 8 ? Math.max(fontSize, minSize) : 12;
}
private onKeyDown(keyboardEvent: IKeyboardEvent): void {
switch (keyboardEvent.keyCode) {
case KeyCode.Enter:
case KeyCode.Space:
this.toggle();
break;
case KeyCode.LeftArrow:
this.collapse(true);
break;
case KeyCode.RightArrow:
this.collapse(false);
break;
case KeyCode.UpArrow:
if (this.settingsGroup.range.startLineNumber - 3 !== 1) {
this.editor.focus();
const lineNumber = this.settingsGroup.range.startLineNumber - 2;
if (this.editor.hasModel()) {
this.editor.setPosition({ lineNumber, column: this.editor.getModel().getLineMinColumn(lineNumber) });
}
}
break;
case KeyCode.DownArrow:
const lineNumber = this.isCollapsed() ? this.settingsGroup.range.startLineNumber : this.settingsGroup.range.startLineNumber - 1;
this.editor.focus();
if (this.editor.hasModel()) {
this.editor.setPosition({ lineNumber, column: this.editor.getModel().getLineMinColumn(lineNumber) });
}
break;
}
}
private toggle() {
this.collapse(!this.isCollapsed());
}
private collapse(collapse: boolean) {
if (collapse !== this.isCollapsed()) {
DOM.toggleClass(this.titleContainer, 'collapsed', collapse);
this._onToggled.fire(collapse);
}
}
private onCursorChange(e: ICursorPositionChangedEvent): void {
if (e.source !== 'mouse' && this.focusTitle(e.position)) {
this.titleContainer.focus();
}
}
private focusTitle(currentPosition: Position): boolean {
const previousPosition = this.previousPosition;
this.previousPosition = currentPosition;
if (!previousPosition) {
return false;
}
if (previousPosition.lineNumber === currentPosition.lineNumber) {
return false;
}
if (!this.settingsGroup.range) {
// #60460?
return false;
}
if (currentPosition.lineNumber === this.settingsGroup.range.startLineNumber - 1 || currentPosition.lineNumber === this.settingsGroup.range.startLineNumber - 2) {
return true;
}
if (this.isCollapsed() && currentPosition.lineNumber === this.settingsGroup.range.endLineNumber) {
return true;
}
return false;
}
dispose() {
this.editor.changeViewZones(accessor => {
accessor.removeZone(this.id);
});
super.dispose();
}
}
export class FolderSettingsActionItem extends BaseActionItem {
private _folder: IWorkspaceFolder | null;
private _folderSettingCounts = new Map<string, number>();
private container: HTMLElement;
private anchorElement: HTMLElement;
private labelElement: HTMLElement;
private detailsElement: HTMLElement;
private dropDownElement: HTMLElement;
private disposables: IDisposable[] = [];
constructor(
action: IAction,
@IWorkspaceContextService private readonly contextService: IWorkspaceContextService,
@IContextMenuService private readonly contextMenuService: IContextMenuService
) {
super(null, action);
const workspace = this.contextService.getWorkspace();
this._folder = workspace.folders.length === 1 ? workspace.folders[0] : null;
this.disposables.push(this.contextService.onDidChangeWorkspaceFolders(() => this.onWorkspaceFoldersChanged()));
}
get folder(): IWorkspaceFolder | null {
return this._folder;
}
set folder(folder: IWorkspaceFolder | null) {
this._folder = folder;
this.update();
}
setCount(settingsTarget: URI, count: number): void {
const workspaceFolder = this.contextService.getWorkspaceFolder(settingsTarget);
if (!workspaceFolder) {
throw new Error('unknown folder');
}
const folder = workspaceFolder.uri;
this._folderSettingCounts.set(folder.toString(), count);
this.update();
}
render(container: HTMLElement): void {
this.element = container;
this.container = container;
this.labelElement = DOM.$('.action-title');
this.detailsElement = DOM.$('.action-details');
this.dropDownElement = DOM.$('.dropdown-icon.octicon.octicon-triangle-down.hide');
this.anchorElement = DOM.$('a.action-label.folder-settings', {
role: 'button',
'aria-haspopup': 'true',
'tabindex': '0'
}, this.labelElement, this.detailsElement, this.dropDownElement);
this._register(DOM.addDisposableListener(this.anchorElement, DOM.EventType.MOUSE_DOWN, e => DOM.EventHelper.stop(e)));
this.disposables.push(DOM.addDisposableListener(this.anchorElement, DOM.EventType.CLICK, e => this.onClick(e)));
this.disposables.push(DOM.addDisposableListener(this.anchorElement, DOM.EventType.KEY_UP, e => this.onKeyUp(e)));
DOM.append(this.container, this.anchorElement);
this.update();
}
private onKeyUp(event: any): void {
const keyboardEvent = new StandardKeyboardEvent(event);
switch (keyboardEvent.keyCode) {
case KeyCode.Enter:
case KeyCode.Space:
this.onClick(event);
return;
}
}
onClick(event: DOM.EventLike): void {
DOM.EventHelper.stop(event, true);
if (!this.folder || this._action.checked) {
this.showMenu();
} else {
this._action.run(this._folder);
}
}
protected updateEnabled(): void {
this.update();
}
protected updateChecked(): void {
this.update();
}
private onWorkspaceFoldersChanged(): void {
const oldFolder = this._folder;
const workspace = this.contextService.getWorkspace();
if (oldFolder) {
this._folder = workspace.folders.filter(folder => folder.uri.toString() === oldFolder.uri.toString())[0] || workspace.folders[0];
}
this._folder = this._folder ? this._folder : workspace.folders.length === 1 ? workspace.folders[0] : null;
this.update();
if (this._action.checked) {
if ((oldFolder || !this._folder)
|| (!oldFolder || this._folder)
|| (oldFolder && this._folder && (oldFolder as IWorkspaceFolder).uri.toString() === (this._folder as IWorkspaceFolder).uri.toString())) {
this._action.run(this._folder);
}
}
}
private update(): void {
let total = 0;
this._folderSettingCounts.forEach(n => total += n);
const workspace = this.contextService.getWorkspace();
if (this._folder) {
this.labelElement.textContent = this._folder.name;
this.anchorElement.title = this._folder.name;
const detailsText = this.labelWithCount(this._action.label, total);
this.detailsElement.textContent = detailsText;
DOM.toggleClass(this.dropDownElement, 'hide', workspace.folders.length === 1 || !this._action.checked);
} else {
const labelText = this.labelWithCount(this._action.label, total);
this.labelElement.textContent = labelText;
this.detailsElement.textContent = '';
this.anchorElement.title = this._action.label;
DOM.removeClass(this.dropDownElement, 'hide');
}
DOM.toggleClass(this.anchorElement, 'checked', this._action.checked);
DOM.toggleClass(this.container, 'disabled', !this._action.enabled);
}
private showMenu(): void {
this.contextMenuService.showContextMenu({
getAnchor: () => this.container,
getActions: () => this.getDropdownMenuActions(),
getActionItem: () => null,
onHide: () => {
this.anchorElement.blur();
}
});
}
private getDropdownMenuActions(): IAction[] {
const actions: IAction[] = [];
const workspaceFolders = this.contextService.getWorkspace().folders;
if (this.contextService.getWorkbenchState() === WorkbenchState.WORKSPACE && workspaceFolders.length > 0) {
actions.push(...workspaceFolders.map((folder, index) => {
const folderCount = this._folderSettingCounts.get(folder.uri.toString());
return <IAction>{
id: 'folderSettingsTarget' + index,
label: this.labelWithCount(folder.name, folderCount),
checked: this.folder && this.folder.uri.toString() === folder.uri.toString(),
enabled: true,
run: () => this._action.run(folder)
};
}));
}
return actions;
}
private labelWithCount(label: string, count: number | undefined): string {
// Append the count if it's >0 and not undefined
if (count) {
label += ` (${count})`;
}
return label;
}
dispose(): void {
dispose(this.disposables);
super.dispose();
}
}
export type SettingsTarget = ConfigurationTarget.USER | ConfigurationTarget.WORKSPACE | URI;
export class SettingsTargetsWidget extends Widget {
private settingsSwitcherBar: ActionBar;
private userSettings: Action;
private workspaceSettings: Action;
private folderSettings: FolderSettingsActionItem;
private _settingsTarget: SettingsTarget;
private readonly _onDidTargetChange = new Emitter<SettingsTarget>();
readonly onDidTargetChange: Event<SettingsTarget> = this._onDidTargetChange.event;
constructor(
parent: HTMLElement,
@IWorkspaceContextService private readonly contextService: IWorkspaceContextService,
@IInstantiationService private readonly instantiationService: IInstantiationService
) {
super();
this.create(parent);
this._register(this.contextService.onDidChangeWorkbenchState(() => this.onWorkbenchStateChanged()));
this._register(this.contextService.onDidChangeWorkspaceFolders(() => this.update()));
}
private create(parent: HTMLElement): void {
const settingsTabsWidget = DOM.append(parent, DOM.$('.settings-tabs-widget'));
this.settingsSwitcherBar = this._register(new ActionBar(settingsTabsWidget, {
orientation: ActionsOrientation.HORIZONTAL,
ariaLabel: localize('settingsSwitcherBarAriaLabel', "Settings Switcher"),
animated: false,
actionItemProvider: (action: Action) => action.id === 'folderSettings' ? this.folderSettings : null
}));
this.userSettings = new Action('userSettings', localize('userSettings', "User Settings"), '.settings-tab', true, () => this.updateTarget(ConfigurationTarget.USER));
this.userSettings.tooltip = this.userSettings.label;
this.workspaceSettings = new Action('workspaceSettings', localize('workspaceSettings', "Workspace Settings"), '.settings-tab', false, () => this.updateTarget(ConfigurationTarget.WORKSPACE));
this.workspaceSettings.tooltip = this.workspaceSettings.label;
const folderSettingsAction = new Action('folderSettings', localize('folderSettings', "Folder Settings"), '.settings-tab', false, (folder: IWorkspaceFolder) => this.updateTarget(folder ? folder.uri : ConfigurationTarget.USER));
this.folderSettings = this.instantiationService.createInstance(FolderSettingsActionItem, folderSettingsAction);
this.update();
this.settingsSwitcherBar.push([this.userSettings, this.workspaceSettings, folderSettingsAction]);
}
get settingsTarget(): SettingsTarget {
return this._settingsTarget;
}
set settingsTarget(settingsTarget: SettingsTarget) {
this._settingsTarget = settingsTarget;
this.userSettings.checked = ConfigurationTarget.USER === this.settingsTarget;
this.workspaceSettings.checked = ConfigurationTarget.WORKSPACE === this.settingsTarget;
if (this.settingsTarget instanceof URI) {
this.folderSettings.getAction().checked = true;
this.folderSettings.folder = this.contextService.getWorkspaceFolder(this.settingsTarget as URI);
} else {
this.folderSettings.getAction().checked = false;
}
}
setResultCount(settingsTarget: SettingsTarget, count: number): void {
if (settingsTarget === ConfigurationTarget.WORKSPACE) {
let label = localize('workspaceSettings', "Workspace Settings");
if (count) {
label += ` (${count})`;
}
this.workspaceSettings.label = label;
} else if (settingsTarget === ConfigurationTarget.USER) {
let label = localize('userSettings', "User Settings");
if (count) {
label += ` (${count})`;
}
this.userSettings.label = label;
} else if (settingsTarget instanceof URI) {
this.folderSettings.setCount(settingsTarget, count);
}
}
private onWorkbenchStateChanged(): void {
this.folderSettings.folder = null;
this.update();
if (this.settingsTarget === ConfigurationTarget.WORKSPACE && this.contextService.getWorkbenchState() === WorkbenchState.WORKSPACE) {
this.updateTarget(ConfigurationTarget.USER);
}
}
updateTarget(settingsTarget: SettingsTarget): Promise<void> {
const isSameTarget = this.settingsTarget === settingsTarget || settingsTarget instanceof URI && this.settingsTarget instanceof URI && this.settingsTarget.toString() === settingsTarget.toString();
if (!isSameTarget) {
this.settingsTarget = settingsTarget;
this._onDidTargetChange.fire(this.settingsTarget);
}
return Promise.resolve(undefined);
}
private update(): void {
DOM.toggleClass(this.settingsSwitcherBar.domNode, 'empty-workbench', this.contextService.getWorkbenchState() === WorkbenchState.EMPTY);
this.workspaceSettings.enabled = this.contextService.getWorkbenchState() !== WorkbenchState.EMPTY;
this.folderSettings.getAction().enabled = this.contextService.getWorkbenchState() === WorkbenchState.WORKSPACE && this.contextService.getWorkspace().folders.length > 0;
}
}
export interface SearchOptions extends IInputOptions {
focusKey?: IContextKey<boolean>;
showResultCount?: boolean;
ariaLive?: string;
ariaLabelledBy?: string;
}
export class SearchWidget extends Widget {
domNode: HTMLElement;
private countElement: HTMLElement;
private searchContainer: HTMLElement;
inputBox: InputBox;
private controlsDiv: HTMLElement;
private readonly _onDidChange: Emitter<string> = this._register(new Emitter<string>());
readonly onDidChange: Event<string> = this._onDidChange.event;
private readonly _onFocus: Emitter<void> = this._register(new Emitter<void>());
readonly onFocus: Event<void> = this._onFocus.event;
constructor(parent: HTMLElement, protected options: SearchOptions,
@IContextViewService private readonly contextViewService: IContextViewService,
@IInstantiationService protected instantiationService: IInstantiationService,
@IThemeService private readonly themeService: IThemeService
) {
super();
this.create(parent);
}
private create(parent: HTMLElement) {
this.domNode = DOM.append(parent, DOM.$('div.settings-header-widget'));
this.createSearchContainer(DOM.append(this.domNode, DOM.$('div.settings-search-container')));
this.controlsDiv = DOM.append(this.domNode, DOM.$('div.settings-search-controls'));
if (this.options.showResultCount) {
this.countElement = DOM.append(this.controlsDiv, DOM.$('.settings-count-widget'));
this._register(attachStylerCallback(this.themeService, { badgeBackground, contrastBorder }, colors => {
const background = colors.badgeBackground ? colors.badgeBackground.toString() : null;
const border = colors.contrastBorder ? colors.contrastBorder.toString() : null;
this.countElement.style.backgroundColor = background;
this.countElement.style.borderWidth = border ? '1px' : null;
this.countElement.style.borderStyle = border ? 'solid' : null;
this.countElement.style.borderColor = border;
const color = this.themeService.getTheme().getColor(badgeForeground);
this.countElement.style.color = color ? color.toString() : null;
}));
}
this.inputBox.inputElement.setAttribute('aria-live', this.options.ariaLive || 'off');
if (this.options.ariaLabelledBy) {
this.inputBox.inputElement.setAttribute('aria-labelledBy', this.options.ariaLabelledBy);
}
const focusTracker = this._register(DOM.trackFocus(this.inputBox.inputElement));
this._register(focusTracker.onDidFocus(() => this._onFocus.fire()));
const focusKey = this.options.focusKey;
if (focusKey) {
this._register(focusTracker.onDidFocus(() => focusKey.set(true)));
this._register(focusTracker.onDidBlur(() => focusKey.set(false)));
}
}
private createSearchContainer(searchContainer: HTMLElement) {
this.searchContainer = searchContainer;
const searchInput = DOM.append(this.searchContainer, DOM.$('div.settings-search-input'));
this.inputBox = this._register(this.createInputBox(searchInput));
this._register(this.inputBox.onDidChange(value => this._onDidChange.fire(value)));
}
protected createInputBox(parent: HTMLElement): InputBox {
const box = this._register(new InputBox(parent, this.contextViewService, this.options));
this._register(attachInputBoxStyler(box, this.themeService));
return box;
}
showMessage(message: string): void {
// Avoid setting the aria-label unnecessarily, the screenreader will read the count every time it's set, since it's aria-live:assertive. #50968
if (this.countElement && message !== this.countElement.textContent) {
this.countElement.textContent = message;
this.inputBox.inputElement.setAttribute('aria-label', message);
this.inputBox.inputElement.style.paddingRight = this.getControlsWidth() + 'px';
}
}
layout(dimension: DOM.Dimension) {
if (dimension.width < 400) {
if (this.countElement) {
DOM.addClass(this.countElement, 'hide');
}
this.inputBox.inputElement.style.paddingRight = '0px';
} else {
if (this.countElement) {
DOM.removeClass(this.countElement, 'hide');
}
this.inputBox.inputElement.style.paddingRight = this.getControlsWidth() + 'px';
}
}
private getControlsWidth(): number {
const countWidth = this.countElement ? DOM.getTotalWidth(this.countElement) : 0;
return countWidth + 20;
}
focus() {
this.inputBox.focus();
if (this.getValue()) {
this.inputBox.select();
}
}
hasFocus(): boolean {
return this.inputBox.hasFocus();
}
clear() {
this.inputBox.value = '';
}
getValue(): string {
return this.inputBox.value;
}
setValue(value: string): string {
return this.inputBox.value = value;
}
dispose(): void {
if (this.options.focusKey) {
this.options.focusKey.set(false);
}
super.dispose();
}
}
export class EditPreferenceWidget<T> extends Disposable {
static readonly GLYPH_MARGIN_CLASS_NAME = 'edit-preferences-widget';
private _line: number;
private _preferences: T[];
private _editPreferenceDecoration: string[];
private readonly _onClick = new Emitter<IEditorMouseEvent>();
get onClick(): Event<IEditorMouseEvent> { return this._onClick.event; }
constructor(private editor: ICodeEditor
) {
super();
this._editPreferenceDecoration = [];
this._register(this.editor.onMouseDown((e: IEditorMouseEvent) => {
const data = e.target.detail as IMarginData;
if (e.target.type !== MouseTargetType.GUTTER_GLYPH_MARGIN || data.isAfterLines || !this.isVisible()) {
return;
}
this._onClick.fire(e);
}));
}
get preferences(): T[] {
return this._preferences;
}
getLine(): number {
return this._line;
}
show(line: number, hoverMessage: string, preferences: T[]): void {
this._preferences = preferences;
const newDecoration: IModelDeltaDecoration[] = [];
this._line = line;
newDecoration.push({
options: {
glyphMarginClassName: EditPreferenceWidget.GLYPH_MARGIN_CLASS_NAME,
glyphMarginHoverMessage: new MarkdownString().appendText(hoverMessage),
stickiness: TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges,
},
range: {
startLineNumber: line,
startColumn: 1,
endLineNumber: line,
endColumn: 1
}
});
this._editPreferenceDecoration = this.editor.deltaDecorations(this._editPreferenceDecoration, newDecoration);
}
hide(): void {
this._editPreferenceDecoration = this.editor.deltaDecorations(this._editPreferenceDecoration, []);
}
isVisible(): boolean {
return this._editPreferenceDecoration.length > 0;
}
dispose(): void {
this.hide();
super.dispose();
}
}
registerThemingParticipant((theme: ITheme, collector: ICssStyleCollector) => {
collector.addRule(`
.settings-tabs-widget > .monaco-action-bar .action-item .action-label:focus,
.settings-tabs-widget > .monaco-action-bar .action-item .action-label.checked {
border-bottom: 1px solid;
}
`);
// Title Active
const titleActive = theme.getColor(PANEL_ACTIVE_TITLE_FOREGROUND);
const titleActiveBorder = theme.getColor(PANEL_ACTIVE_TITLE_BORDER);
if (titleActive || titleActiveBorder) {
collector.addRule(`
.settings-tabs-widget > .monaco-action-bar .action-item .action-label:hover,
.settings-tabs-widget > .monaco-action-bar .action-item .action-label.checked {
color: ${titleActive};
border-bottom-color: ${titleActiveBorder};
}
`);
}
// Title Inactive
const titleInactive = theme.getColor(PANEL_INACTIVE_TITLE_FOREGROUND);
if (titleInactive) {
collector.addRule(`
.settings-tabs-widget > .monaco-action-bar .action-item .action-label {
color: ${titleInactive};
}
`);
}
// Title focus
const focusBorderColor = theme.getColor(focusBorder);
if (focusBorderColor) {
collector.addRule(`
.settings-tabs-widget > .monaco-action-bar .action-item .action-label:focus {
border-bottom-color: ${focusBorderColor} !important;
}
`);
collector.addRule(`
.settings-tabs-widget > .monaco-action-bar .action-item .action-label:focus {
outline: none;
}
`);
}
// Styling with Outline color (e.g. high contrast theme)
const outline = theme.getColor(activeContrastBorder);
if (outline) {
const outline = theme.getColor(activeContrastBorder);
collector.addRule(`
.settings-tabs-widget > .monaco-action-bar .action-item .action-label.checked,
.settings-tabs-widget > .monaco-action-bar .action-item .action-label:hover {
outline-color: ${outline};
outline-width: 1px;
outline-style: solid;
border-bottom: none;
padding-bottom: 0;
outline-offset: -1px;
}
.settings-tabs-widget > .monaco-action-bar .action-item .action-label:not(.checked):hover {
outline-style: dashed;
}
`);
}
});

View File

@@ -0,0 +1,206 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { localize } from 'vs/nls';
import { ISetting } from 'vs/workbench/services/preferences/common/preferences';
export interface ITOCEntry {
id: string;
label: string;
children?: ITOCEntry[];
settings?: Array<string | ISetting>;
}
export const commonlyUsedData: ITOCEntry = {
id: 'commonlyUsed',
label: localize('commonlyUsed', "Commonly Used"),
settings: ['files.autoSave', 'editor.fontSize', 'editor.fontFamily', 'editor.tabSize', 'editor.renderWhitespace', 'editor.cursorStyle', 'editor.multiCursorModifier', 'editor.insertSpaces', 'editor.wordWrap', 'files.exclude', 'files.associations']
};
export const tocData: ITOCEntry = {
id: 'root',
label: 'root',
children: [
{
id: 'editor',
label: localize('textEditor', "Text Editor"),
settings: ['editor.*'],
children: [
{
id: 'editor/cursor',
label: localize('cursor', "Cursor"),
settings: ['editor.cursor*']
},
{
id: 'editor/find',
label: localize('find', "Find"),
settings: ['editor.find.*']
},
{
id: 'editor/font',
label: localize('font', "Font"),
settings: ['editor.font*']
},
{
id: 'editor/format',
label: localize('formatting', "Formatting"),
settings: ['editor.format*']
},
{
id: 'editor/diffEditor',
label: localize('diffEditor', "Diff Editor"),
settings: ['diffEditor.*']
},
{
id: 'editor/minimap',
label: localize('minimap', "Minimap"),
settings: ['editor.minimap.*']
},
{
id: 'editor/suggestions',
label: localize('suggestions', "Suggestions"),
settings: ['editor.*suggest*']
},
{
id: 'editor/files',
label: localize('files', "Files"),
settings: ['files.*']
}
]
},
{
id: 'workbench',
label: localize('workbench', "Workbench"),
settings: ['workbench.*'],
children: [
{
id: 'workbench/appearance',
label: localize('appearance', "Appearance"),
settings: ['workbench.activityBar.*', 'workbench.*color*', 'workbench.fontAliasing', 'workbench.iconTheme', 'workbench.sidebar.location', 'workbench.*.visible', 'workbench.tips.enabled', 'workbench.tree.*', 'workbench.view.*']
},
{
id: 'workbench/breadcrumbs',
label: localize('breadcrumbs', "Breadcrumbs"),
settings: ['breadcrumbs.*']
},
{
id: 'workbench/editor',
label: localize('editorManagement', "Editor Management"),
settings: ['workbench.editor.*']
},
{
id: 'workbench/settings',
label: localize('settings', "Settings Editor"),
settings: ['workbench.settings.*']
},
{
id: 'workbench/zenmode',
label: localize('zenMode', "Zen Mode"),
settings: ['zenmode.*']
}
]
},
{
id: 'window',
label: localize('window', "Window"),
settings: ['window.*'],
children: [
{
id: 'window/newWindow',
label: localize('newWindow', "New Window"),
settings: ['window.*newwindow*']
}
]
},
{
id: 'features',
label: localize('features', "Features"),
children: [
{
id: 'features/explorer',
label: localize('fileExplorer', "Explorer"),
settings: ['explorer.*', 'outline.*']
},
{
id: 'features/search',
label: localize('search', "Search"),
settings: ['search.*', 'searchRipgrep.*']
}
,
{
id: 'features/debug',
label: localize('debug', "Debug"),
settings: ['debug.*', 'launch']
},
{
id: 'features/scm',
label: localize('scm', "SCM"),
settings: ['scm.*']
},
{
id: 'features/extensions',
label: localize('extensionViewlet', "Extension Viewlet"),
settings: ['extensions.*']
},
{
id: 'features/terminal',
label: localize('terminal', "Terminal"),
settings: ['terminal.*']
},
{
id: 'features/problems',
label: localize('problems', "Problems"),
settings: ['problems.*']
},
{
id: 'features/comments',
label: localize('comments', "Comments"),
settings: ['comments.*']
}
]
},
{
id: 'application',
label: localize('application', "Application"),
children: [
{
id: 'application/http',
label: localize('proxy', "Proxy"),
settings: ['http.*']
},
{
id: 'application/keyboard',
label: localize('keyboard', "Keyboard"),
settings: ['keyboard.*']
},
{
id: 'application/update',
label: localize('update', "Update"),
settings: ['update.*']
},
{
id: 'application/telemetry',
label: localize('telemetry', "Telemetry"),
settings: ['telemetry.*']
}
]
}
]
};
export const knownAcronyms = new Set();
[
'css',
'html',
'scss',
'less',
'json',
'js',
'ts',
'ie',
'id',
'php',
].forEach(str => knownAcronyms.add(str));

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,551 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as arrays from 'vs/base/common/arrays';
import { isArray } from 'vs/base/common/types';
import { URI } from 'vs/base/common/uri';
import { localize } from 'vs/nls';
import { ConfigurationTarget, IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { ConfigurationScope } from 'vs/platform/configuration/common/configurationRegistry';
import { SettingsTarget } from 'vs/workbench/contrib/preferences/browser/preferencesWidgets';
import { ITOCEntry, knownAcronyms } from 'vs/workbench/contrib/preferences/browser/settingsLayout';
import { IExtensionSetting, ISearchResult, ISetting, SettingValueType } from 'vs/workbench/services/preferences/common/preferences';
import { MODIFIED_SETTING_TAG } from 'vs/workbench/contrib/preferences/common/preferences';
export const ONLINE_SERVICES_SETTING_TAG = 'usesOnlineServices';
export interface ISettingsEditorViewState {
settingsTarget: SettingsTarget;
tagFilters?: Set<string>;
filterToCategory?: SettingsTreeGroupElement;
}
export abstract class SettingsTreeElement {
id: string;
parent?: SettingsTreeGroupElement;
/**
* Index assigned in display order, used for paging.
*/
index: number;
}
export type SettingsTreeGroupChild = (SettingsTreeGroupElement | SettingsTreeSettingElement | SettingsTreeNewExtensionsElement);
export class SettingsTreeGroupElement extends SettingsTreeElement {
count?: number;
label: string;
level: number;
isFirstGroup: boolean;
private _childSettingKeys: Set<string>;
private _children: SettingsTreeGroupChild[];
get children(): SettingsTreeGroupChild[] {
return this._children;
}
set children(newChildren: SettingsTreeGroupChild[]) {
this._children = newChildren;
this._childSettingKeys = new Set();
this._children.forEach(child => {
if (child instanceof SettingsTreeSettingElement) {
this._childSettingKeys.add(child.setting.key);
}
});
}
/**
* Returns whether this group contains the given child key (to a depth of 1 only)
*/
containsSetting(key: string): boolean {
return this._childSettingKeys.has(key);
}
}
export class SettingsTreeNewExtensionsElement extends SettingsTreeElement {
extensionIds: string[];
}
export class SettingsTreeSettingElement extends SettingsTreeElement {
private static MAX_DESC_LINES = 20;
setting: ISetting;
private _displayCategory: string;
private _displayLabel: string;
/**
* scopeValue || defaultValue, for rendering convenience.
*/
value: any;
/**
* The value in the current settings scope.
*/
scopeValue: any;
/**
* The default value
*/
defaultValue?: any;
/**
* Whether the setting is configured in the selected scope.
*/
isConfigured: boolean;
tags?: Set<string>;
overriddenScopeList: string[];
description: string;
valueType: SettingValueType;
constructor(setting: ISetting, parent: SettingsTreeGroupElement, index: number, inspectResult: IInspectResult) {
super();
this.index = index;
this.setting = setting;
this.parent = parent;
this.id = sanitizeId(parent.id + '_' + setting.key);
this.update(inspectResult);
}
get displayCategory(): string {
if (!this._displayCategory) {
this.initLabel();
}
return this._displayCategory;
}
get displayLabel(): string {
if (!this._displayLabel) {
this.initLabel();
}
return this._displayLabel;
}
private initLabel(): void {
const displayKeyFormat = settingKeyToDisplayFormat(this.setting.key, this.parent!.id);
this._displayLabel = displayKeyFormat.label;
this._displayCategory = displayKeyFormat.category;
}
update(inspectResult: IInspectResult): void {
const { isConfigured, inspected, targetSelector } = inspectResult;
const displayValue = isConfigured ? inspected[targetSelector] : inspected.default;
const overriddenScopeList: string[] = [];
if (targetSelector === 'user' && typeof inspected.workspace !== 'undefined') {
overriddenScopeList.push(localize('workspace', "Workspace"));
}
if (targetSelector === 'workspace' && typeof inspected.user !== 'undefined') {
overriddenScopeList.push(localize('user', "User"));
}
this.value = displayValue;
this.scopeValue = isConfigured && inspected[targetSelector];
this.defaultValue = inspected.default;
this.isConfigured = isConfigured;
if (isConfigured || this.setting.tags || this.tags) {
// Don't create an empty Set for all 1000 settings, only if needed
this.tags = new Set<string>();
if (isConfigured) {
this.tags.add(MODIFIED_SETTING_TAG);
}
if (this.setting.tags) {
this.setting.tags.forEach(tag => this.tags!.add(tag));
}
}
this.overriddenScopeList = overriddenScopeList;
if (this.setting.description.length > SettingsTreeSettingElement.MAX_DESC_LINES) {
const truncatedDescLines = this.setting.description.slice(0, SettingsTreeSettingElement.MAX_DESC_LINES);
truncatedDescLines.push('[...]');
this.description = truncatedDescLines.join('\n');
} else {
this.description = this.setting.description.join('\n');
}
if (this.setting.enum && (!this.setting.type || settingTypeEnumRenderable(this.setting.type))) {
this.valueType = SettingValueType.Enum;
} else if (this.setting.type === 'string') {
this.valueType = SettingValueType.String;
} else if (isExcludeSetting(this.setting)) {
this.valueType = SettingValueType.Exclude;
} else if (this.setting.type === 'integer') {
this.valueType = SettingValueType.Integer;
} else if (this.setting.type === 'number') {
this.valueType = SettingValueType.Number;
} else if (this.setting.type === 'boolean') {
this.valueType = SettingValueType.Boolean;
} else if (isArray(this.setting.type) && this.setting.type.indexOf(SettingValueType.Null) > -1 && this.setting.type.length === 2) {
if (this.setting.type.indexOf(SettingValueType.Integer) > -1) {
this.valueType = SettingValueType.NullableInteger;
} else if (this.setting.type.indexOf(SettingValueType.Number) > -1) {
this.valueType = SettingValueType.NullableNumber;
} else {
this.valueType = SettingValueType.Complex;
}
} else {
this.valueType = SettingValueType.Complex;
}
}
matchesAllTags(tagFilters?: Set<string>): boolean {
if (!tagFilters || !tagFilters.size) {
return true;
}
if (this.tags) {
let hasFilteredTag = true;
tagFilters.forEach(tag => {
hasFilteredTag = hasFilteredTag && this.tags!.has(tag);
});
return hasFilteredTag;
} else {
return false;
}
}
matchesScope(scope: SettingsTarget): boolean {
const configTarget = URI.isUri(scope) ? ConfigurationTarget.WORKSPACE_FOLDER : scope;
if (configTarget === ConfigurationTarget.WORKSPACE_FOLDER) {
return this.setting.scope === ConfigurationScope.RESOURCE;
}
if (configTarget === ConfigurationTarget.WORKSPACE) {
return this.setting.scope === ConfigurationScope.WINDOW || this.setting.scope === ConfigurationScope.RESOURCE;
}
return true;
}
}
export class SettingsTreeModel {
protected _root: SettingsTreeGroupElement;
protected _treeElementsById = new Map<string, SettingsTreeElement>();
private _treeElementsBySettingName = new Map<string, SettingsTreeSettingElement[]>();
private _tocRoot: ITOCEntry;
constructor(
protected _viewState: ISettingsEditorViewState,
@IConfigurationService private readonly _configurationService: IConfigurationService
) { }
get root(): SettingsTreeGroupElement {
return this._root;
}
update(newTocRoot = this._tocRoot): void {
this._treeElementsById.clear();
this._treeElementsBySettingName.clear();
const newRoot = this.createSettingsTreeGroupElement(newTocRoot);
if (newRoot.children[0] instanceof SettingsTreeGroupElement) {
(<SettingsTreeGroupElement>newRoot.children[0]).isFirstGroup = true; // TODO
}
if (this._root) {
this._root.children = newRoot.children;
} else {
this._root = newRoot;
}
}
getElementById(id: string): SettingsTreeElement | null {
return this._treeElementsById.get(id) || null;
}
getElementsByName(name: string): SettingsTreeSettingElement[] | null {
return this._treeElementsBySettingName.get(name) || null;
}
updateElementsByName(name: string): void {
if (!this._treeElementsBySettingName.has(name)) {
return;
}
this._treeElementsBySettingName.get(name)!.forEach(element => {
const inspectResult = inspectSetting(element.setting.key, this._viewState.settingsTarget, this._configurationService);
element.update(inspectResult);
});
}
private createSettingsTreeGroupElement(tocEntry: ITOCEntry, parent?: SettingsTreeGroupElement): SettingsTreeGroupElement {
const element = new SettingsTreeGroupElement();
const index = this._treeElementsById.size;
element.index = index;
element.id = tocEntry.id;
element.label = tocEntry.label;
element.parent = parent;
element.level = this.getDepth(element);
const children: SettingsTreeGroupChild[] = [];
if (tocEntry.settings) {
const settingChildren = tocEntry.settings.map(s => this.createSettingsTreeSettingElement(<ISetting>s, element))
.filter(el => el.setting.deprecationMessage ? el.isConfigured : true);
children.push(...settingChildren);
}
if (tocEntry.children) {
const groupChildren = tocEntry.children.map(child => this.createSettingsTreeGroupElement(child, element));
children.push(...groupChildren);
}
element.children = children;
this._treeElementsById.set(element.id, element);
return element;
}
private getDepth(element: SettingsTreeElement): number {
if (element.parent) {
return 1 + this.getDepth(element.parent);
} else {
return 0;
}
}
private createSettingsTreeSettingElement(setting: ISetting, parent: SettingsTreeGroupElement): SettingsTreeSettingElement {
const index = this._treeElementsById.size;
const inspectResult = inspectSetting(setting.key, this._viewState.settingsTarget, this._configurationService);
const element = new SettingsTreeSettingElement(setting, parent, index, inspectResult);
this._treeElementsById.set(element.id, element);
const nameElements = this._treeElementsBySettingName.get(setting.key) || [];
nameElements.push(element);
this._treeElementsBySettingName.set(setting.key, nameElements);
return element;
}
}
interface IInspectResult {
isConfigured: boolean;
inspected: any;
targetSelector: string;
}
function inspectSetting(key: string, target: SettingsTarget, configurationService: IConfigurationService): IInspectResult {
const inspectOverrides = URI.isUri(target) ? { resource: target } : undefined;
const inspected = configurationService.inspect(key, inspectOverrides);
const targetSelector = target === ConfigurationTarget.USER ? 'user' :
target === ConfigurationTarget.WORKSPACE ? 'workspace' :
'workspaceFolder';
const isConfigured = typeof inspected[targetSelector] !== 'undefined';
return { isConfigured, inspected, targetSelector };
}
function sanitizeId(id: string): string {
return id.replace(/[\.\/]/, '_');
}
export function settingKeyToDisplayFormat(key: string, groupId = ''): { category: string, label: string } {
const lastDotIdx = key.lastIndexOf('.');
let category = '';
if (lastDotIdx >= 0) {
category = key.substr(0, lastDotIdx);
key = key.substr(lastDotIdx + 1);
}
groupId = groupId.replace(/\//g, '.');
category = trimCategoryForGroup(category, groupId);
category = wordifyKey(category);
const label = wordifyKey(key);
return { category, label };
}
function wordifyKey(key: string): string {
return key
.replace(/\.([a-z])/g, (match, p1) => ` ${p1.toUpperCase()}`)
.replace(/([a-z])([A-Z])/g, '$1 $2') // fooBar => foo Bar
.replace(/^[a-z]/g, match => match.toUpperCase()) // foo => Foo
.replace(/\b\w+\b/g, match => {
return knownAcronyms.has(match.toLowerCase()) ?
match.toUpperCase() :
match;
});
}
function trimCategoryForGroup(category: string, groupId: string): string {
const doTrim = forward => {
const parts = groupId.split('.');
while (parts.length) {
const reg = new RegExp(`^${parts.join('\\.')}(\\.|$)`, 'i');
if (reg.test(category)) {
return category.replace(reg, '');
}
if (forward) {
parts.pop();
} else {
parts.shift();
}
}
return null;
};
let trimmed = doTrim(true);
if (trimmed === null) {
trimmed = doTrim(false);
}
if (trimmed === null) {
trimmed = category;
}
return trimmed;
}
export function isExcludeSetting(setting: ISetting): boolean {
return setting.key === 'files.exclude' ||
setting.key === 'search.exclude' ||
setting.key === 'files.watcherExclude';
}
function settingTypeEnumRenderable(_type: string | string[]) {
const enumRenderableSettingTypes = ['string', 'boolean', 'null', 'integer', 'number'];
const type = isArray(_type) ? _type : [_type];
return type.every(type => enumRenderableSettingTypes.indexOf(type) > -1);
}
export const enum SearchResultIdx {
Local = 0,
Remote = 1,
NewExtensions = 2
}
export class SearchResultModel extends SettingsTreeModel {
private rawSearchResults: ISearchResult[];
private cachedUniqueSearchResults: ISearchResult[] | undefined;
private newExtensionSearchResults: ISearchResult;
readonly id = 'searchResultModel';
constructor(
viewState: ISettingsEditorViewState,
@IConfigurationService configurationService: IConfigurationService
) {
super(viewState, configurationService);
this.update({ id: 'searchResultModel', label: '' });
}
getUniqueResults(): ISearchResult[] {
if (this.cachedUniqueSearchResults) {
return this.cachedUniqueSearchResults;
}
if (!this.rawSearchResults) {
return [];
}
const localMatchKeys = new Set();
const localResult = this.rawSearchResults[SearchResultIdx.Local];
if (localResult) {
localResult.filterMatches.forEach(m => localMatchKeys.add(m.setting.key));
}
const remoteResult = this.rawSearchResults[SearchResultIdx.Remote];
if (remoteResult) {
remoteResult.filterMatches = remoteResult.filterMatches.filter(m => !localMatchKeys.has(m.setting.key));
}
if (remoteResult) {
this.newExtensionSearchResults = this.rawSearchResults[SearchResultIdx.NewExtensions];
}
this.cachedUniqueSearchResults = [localResult, remoteResult];
return this.cachedUniqueSearchResults;
}
getRawResults(): ISearchResult[] {
return this.rawSearchResults;
}
setResult(order: SearchResultIdx, result: ISearchResult | null): void {
this.cachedUniqueSearchResults = undefined;
this.rawSearchResults = this.rawSearchResults || [];
if (!result) {
delete this.rawSearchResults[order];
return;
}
this.rawSearchResults[order] = result;
this.updateChildren();
}
updateChildren(): void {
this.update({
id: 'searchResultModel',
label: 'searchResultModel',
settings: this.getFlatSettings()
});
// Save time, filter children in the search model instead of relying on the tree filter, which still requires heights to be calculated.
this.root.children = this.root.children
.filter(child => child instanceof SettingsTreeSettingElement && child.matchesAllTags(this._viewState.tagFilters) && child.matchesScope(this._viewState.settingsTarget));
if (this.newExtensionSearchResults && this.newExtensionSearchResults.filterMatches.length) {
const newExtElement = new SettingsTreeNewExtensionsElement();
newExtElement.index = this._treeElementsById.size;
newExtElement.parent = this._root;
newExtElement.id = 'newExtensions';
this._treeElementsById.set(newExtElement.id, newExtElement);
const resultExtensionIds = this.newExtensionSearchResults.filterMatches
.map(result => (<IExtensionSetting>result.setting))
.filter(setting => setting.extensionName && setting.extensionPublisher)
.map(setting => `${setting.extensionPublisher}.${setting.extensionName}`);
newExtElement.extensionIds = arrays.distinct(resultExtensionIds);
this._root.children.push(newExtElement);
}
}
private getFlatSettings(): ISetting[] {
const flatSettings: ISetting[] = [];
arrays.coalesce(this.getUniqueResults())
.forEach(r => {
flatSettings.push(
...r.filterMatches.map(m => m.setting));
});
return flatSettings;
}
}
export interface IParsedQuery {
tags: string[];
query: string;
}
const tagRegex = /(^|\s)@tag:("([^"]*)"|[^"]\S*)/g;
export function parseQuery(query: string): IParsedQuery {
const tags: string[] = [];
query = query.replace(tagRegex, (_, __, quotedTag, tag) => {
tags.push(tag || quotedTag);
return '';
});
query = query.replace(`@${MODIFIED_SETTING_TAG}`, () => {
tags.push(MODIFIED_SETTING_TAG);
return '';
});
query = query.trim();
return {
tags,
query
};
}

View File

@@ -0,0 +1,492 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as DOM from 'vs/base/browser/dom';
import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent';
import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar';
import { Button } from 'vs/base/browser/ui/button/button';
import { InputBox } from 'vs/base/browser/ui/inputbox/inputBox';
import { IAction } from 'vs/base/common/actions';
import { Color, RGBA } from 'vs/base/common/color';
import { Emitter, Event } from 'vs/base/common/event';
import { KeyCode } from 'vs/base/common/keyCodes';
import { Disposable, dispose, IDisposable } from 'vs/base/common/lifecycle';
import 'vs/css!./media/settingsWidgets';
import { localize } from 'vs/nls';
import { IContextViewService } from 'vs/platform/contextview/browser/contextView';
import { foreground, inputBackground, inputBorder, inputForeground, listActiveSelectionBackground, listActiveSelectionForeground, listHoverBackground, listHoverForeground, listInactiveSelectionBackground, listInactiveSelectionForeground, registerColor, selectBackground, selectBorder, selectForeground, textLinkForeground, textPreformatForeground, editorWidgetBorder, textLinkActiveForeground } from 'vs/platform/theme/common/colorRegistry';
import { attachButtonStyler, attachInputBoxStyler } from 'vs/platform/theme/common/styler';
import { ICssStyleCollector, ITheme, IThemeService, registerThemingParticipant } from 'vs/platform/theme/common/themeService';
import { disposableTimeout } from 'vs/base/common/async';
const $ = DOM.$;
export const settingsHeaderForeground = registerColor('settings.headerForeground', { light: '#444444', dark: '#e7e7e7', hc: '#ffffff' }, localize('headerForeground', "(For settings editor preview) The foreground color for a section header or active title."));
export const modifiedItemIndicator = registerColor('settings.modifiedItemIndicator', {
light: new Color(new RGBA(102, 175, 224)),
dark: new Color(new RGBA(12, 125, 157)),
hc: new Color(new RGBA(0, 73, 122))
}, localize('modifiedItemForeground', "(For settings editor preview) The color of the modified setting indicator."));
// Enum control colors
export const settingsSelectBackground = registerColor('settings.dropdownBackground', { dark: selectBackground, light: selectBackground, hc: selectBackground }, localize('settingsDropdownBackground', "(For settings editor preview) Settings editor dropdown background."));
export const settingsSelectForeground = registerColor('settings.dropdownForeground', { dark: selectForeground, light: selectForeground, hc: selectForeground }, localize('settingsDropdownForeground', "(For settings editor preview) Settings editor dropdown foreground."));
export const settingsSelectBorder = registerColor('settings.dropdownBorder', { dark: selectBorder, light: selectBorder, hc: selectBorder }, localize('settingsDropdownBorder', "(For settings editor preview) Settings editor dropdown border."));
export const settingsSelectListBorder = registerColor('settings.dropdownListBorder', { dark: editorWidgetBorder, light: editorWidgetBorder, hc: editorWidgetBorder }, localize('settingsDropdownListBorder', "(For settings editor preview) Settings editor dropdown list border. This surrounds the options and separates the options from the description."));
// Bool control colors
export const settingsCheckboxBackground = registerColor('settings.checkboxBackground', { dark: selectBackground, light: selectBackground, hc: selectBackground }, localize('settingsCheckboxBackground', "(For settings editor preview) Settings editor checkbox background."));
export const settingsCheckboxForeground = registerColor('settings.checkboxForeground', { dark: selectForeground, light: selectForeground, hc: selectForeground }, localize('settingsCheckboxForeground', "(For settings editor preview) Settings editor checkbox foreground."));
export const settingsCheckboxBorder = registerColor('settings.checkboxBorder', { dark: selectBorder, light: selectBorder, hc: selectBorder }, localize('settingsCheckboxBorder', "(For settings editor preview) Settings editor checkbox border."));
// Text control colors
export const settingsTextInputBackground = registerColor('settings.textInputBackground', { dark: inputBackground, light: inputBackground, hc: inputBackground }, localize('textInputBoxBackground', "(For settings editor preview) Settings editor text input box background."));
export const settingsTextInputForeground = registerColor('settings.textInputForeground', { dark: inputForeground, light: inputForeground, hc: inputForeground }, localize('textInputBoxForeground', "(For settings editor preview) Settings editor text input box foreground."));
export const settingsTextInputBorder = registerColor('settings.textInputBorder', { dark: inputBorder, light: inputBorder, hc: inputBorder }, localize('textInputBoxBorder', "(For settings editor preview) Settings editor text input box border."));
// Number control colors
export const settingsNumberInputBackground = registerColor('settings.numberInputBackground', { dark: inputBackground, light: inputBackground, hc: inputBackground }, localize('numberInputBoxBackground', "(For settings editor preview) Settings editor number input box background."));
export const settingsNumberInputForeground = registerColor('settings.numberInputForeground', { dark: inputForeground, light: inputForeground, hc: inputForeground }, localize('numberInputBoxForeground', "(For settings editor preview) Settings editor number input box foreground."));
export const settingsNumberInputBorder = registerColor('settings.numberInputBorder', { dark: inputBorder, light: inputBorder, hc: inputBorder }, localize('numberInputBoxBorder', "(For settings editor preview) Settings editor number input box border."));
registerThemingParticipant((theme: ITheme, collector: ICssStyleCollector) => {
const checkboxBackgroundColor = theme.getColor(settingsCheckboxBackground);
if (checkboxBackgroundColor) {
collector.addRule(`.settings-editor > .settings-body > .settings-tree-container .setting-item-bool .setting-value-checkbox { background-color: ${checkboxBackgroundColor} !important; }`);
}
const checkboxBorderColor = theme.getColor(settingsCheckboxBorder);
if (checkboxBorderColor) {
collector.addRule(`.settings-editor > .settings-body > .settings-tree-container .setting-item-bool .setting-value-checkbox { border-color: ${checkboxBorderColor} !important; }`);
}
const link = theme.getColor(textLinkForeground);
if (link) {
collector.addRule(`.settings-editor > .settings-body > .settings-tree-container .setting-item-contents .setting-item-description-markdown a { color: ${link}; }`);
collector.addRule(`.settings-editor > .settings-body > .settings-tree-container .setting-item-contents .setting-item-description-markdown a > code { color: ${link}; }`);
collector.addRule(`.monaco-select-box-dropdown-container > .select-box-details-pane > .select-box-description-markdown a { color: ${link}; }`);
collector.addRule(`.monaco-select-box-dropdown-container > .select-box-details-pane > .select-box-description-markdown a > code { color: ${link}; }`);
}
const activeLink = theme.getColor(textLinkActiveForeground);
if (activeLink) {
collector.addRule(`.settings-editor > .settings-body > .settings-tree-container .setting-item-contents .setting-item-description-markdown a:hover, .settings-editor > .settings-body > .settings-tree-container .setting-item-contents .setting-item-description-markdown a:active { color: ${activeLink}; }`);
collector.addRule(`.settings-editor > .settings-body > .settings-tree-container .setting-item-contents .setting-item-description-markdown a:hover > code, .settings-editor > .settings-body > .settings-tree-container .setting-item-contents .setting-item-description-markdown a:active > code { color: ${activeLink}; }`);
collector.addRule(`.monaco-select-box-dropdown-container > .select-box-details-pane > .select-box-description-markdown a:hover, .monaco-select-box-dropdown-container > .select-box-details-pane > .select-box-description-markdown a:active { color: ${activeLink}; }`);
collector.addRule(`.monaco-select-box-dropdown-container > .select-box-details-pane > .select-box-description-markdown a:hover > code, .monaco-select-box-dropdown-container > .select-box-details-pane > .select-box-description-markdown a:active > code { color: ${activeLink}; }`);
}
const headerForegroundColor = theme.getColor(settingsHeaderForeground);
if (headerForegroundColor) {
collector.addRule(`.settings-editor > .settings-header > .settings-header-controls .settings-tabs-widget .action-label.checked { color: ${headerForegroundColor}; border-bottom-color: ${headerForegroundColor}; }`);
}
const foregroundColor = theme.getColor(foreground);
if (foregroundColor) {
collector.addRule(`.settings-editor > .settings-header > .settings-header-controls .settings-tabs-widget .action-label { color: ${foregroundColor}; }`);
}
// Exclude control
const listHoverBackgroundColor = theme.getColor(listHoverBackground);
if (listHoverBackgroundColor) {
collector.addRule(`.settings-editor > .settings-body > .settings-tree-container .setting-item.setting-item-exclude .setting-exclude-row:hover { background-color: ${listHoverBackgroundColor}; }`);
}
const listHoverForegroundColor = theme.getColor(listHoverForeground);
if (listHoverForegroundColor) {
collector.addRule(`.settings-editor > .settings-body > .settings-tree-container .setting-item.setting-item-exclude .setting-exclude-row:hover { color: ${listHoverForegroundColor}; }`);
}
const listSelectBackgroundColor = theme.getColor(listActiveSelectionBackground);
if (listSelectBackgroundColor) {
collector.addRule(`.settings-editor > .settings-body > .settings-tree-container .setting-item.setting-item-exclude .setting-exclude-row.selected:focus { background-color: ${listSelectBackgroundColor}; }`);
}
const listInactiveSelectionBackgroundColor = theme.getColor(listInactiveSelectionBackground);
if (listInactiveSelectionBackgroundColor) {
collector.addRule(`.settings-editor > .settings-body > .settings-tree-container .setting-item.setting-item-exclude .setting-exclude-row.selected:not(:focus) { background-color: ${listInactiveSelectionBackgroundColor}; }`);
}
const listInactiveSelectionForegroundColor = theme.getColor(listInactiveSelectionForeground);
if (listInactiveSelectionForegroundColor) {
collector.addRule(`.settings-editor > .settings-body > .settings-tree-container .setting-item.setting-item-exclude .setting-exclude-row.selected:not(:focus) { color: ${listInactiveSelectionForegroundColor}; }`);
}
const listSelectForegroundColor = theme.getColor(listActiveSelectionForeground);
if (listSelectForegroundColor) {
collector.addRule(`.settings-editor > .settings-body > .settings-tree-container .setting-item.setting-item-exclude .setting-exclude-row.selected:focus { color: ${listSelectForegroundColor}; }`);
}
const codeTextForegroundColor = theme.getColor(textPreformatForeground);
if (codeTextForegroundColor) {
collector.addRule(`.settings-editor > .settings-body > .settings-tree-container .setting-item .setting-item-description-markdown code { color: ${codeTextForegroundColor} }`);
collector.addRule(`.monaco-select-box-dropdown-container > .select-box-details-pane > .select-box-description-markdown code { color: ${codeTextForegroundColor} }`);
}
const modifiedItemIndicatorColor = theme.getColor(modifiedItemIndicator);
if (modifiedItemIndicatorColor) {
collector.addRule(`.settings-editor > .settings-body > .settings-tree-container .setting-item-contents > .setting-item-modified-indicator { border-color: ${modifiedItemIndicatorColor}; }`);
}
});
export class ExcludeSettingListModel {
private _dataItems: IExcludeDataItem[] = [];
private _editKey: string | null;
private _selectedIdx: number | null;
get items(): IExcludeViewItem[] {
const items = this._dataItems.map((item, i) => {
const editing = item.pattern === this._editKey;
return <IExcludeViewItem>{
...item,
editing,
selected: i === this._selectedIdx || editing
};
});
if (this._editKey === '') {
items.push({
editing: true,
selected: true,
pattern: '',
sibling: ''
});
}
return items;
}
setEditKey(key: string | null): void {
this._editKey = key;
}
setValue(excludeData: IExcludeDataItem[]): void {
this._dataItems = excludeData;
}
select(idx: number): void {
this._selectedIdx = idx;
}
getSelected(): number | null {
return this._selectedIdx;
}
selectNext(): void {
if (typeof this._selectedIdx === 'number') {
this._selectedIdx = Math.min(this._selectedIdx + 1, this._dataItems.length - 1);
} else {
this._selectedIdx = 0;
}
}
selectPrevious(): void {
if (typeof this._selectedIdx === 'number') {
this._selectedIdx = Math.max(this._selectedIdx - 1, 0);
} else {
this._selectedIdx = 0;
}
}
}
export interface IExcludeChangeEvent {
originalPattern: string;
pattern?: string;
sibling?: string;
}
export class ExcludeSettingWidget extends Disposable {
private listElement: HTMLElement;
private listDisposables: IDisposable[] = [];
private model = new ExcludeSettingListModel();
private readonly _onDidChangeExclude = new Emitter<IExcludeChangeEvent>();
readonly onDidChangeExclude: Event<IExcludeChangeEvent> = this._onDidChangeExclude.event;
get domNode(): HTMLElement {
return this.listElement;
}
constructor(
private container: HTMLElement,
@IThemeService private readonly themeService: IThemeService,
@IContextViewService private readonly contextViewService: IContextViewService
) {
super();
this.listElement = DOM.append(container, $('.setting-exclude-widget'));
this.listElement.setAttribute('tabindex', '0');
DOM.append(container, this.renderAddButton());
this.renderList();
this._register(DOM.addDisposableListener(this.listElement, DOM.EventType.CLICK, e => this.onListClick(e)));
this._register(DOM.addDisposableListener(this.listElement, DOM.EventType.DBLCLICK, e => this.onListDoubleClick(e)));
this._register(DOM.addStandardDisposableListener(this.listElement, 'keydown', (e: KeyboardEvent) => {
if (e.keyCode === KeyCode.UpArrow) {
this.model.selectPrevious();
this.renderList();
e.preventDefault();
e.stopPropagation();
} else if (e.keyCode === KeyCode.DownArrow) {
this.model.selectNext();
this.renderList();
e.preventDefault();
e.stopPropagation();
}
}));
}
setValue(excludeData: IExcludeDataItem[]): void {
this.model.setValue(excludeData);
this.renderList();
}
private onListClick(e: MouseEvent): void {
const targetIdx = this.getClickedItemIndex(e);
if (targetIdx < 0) {
return;
}
if (this.model.getSelected() === targetIdx) {
return;
}
this.model.select(targetIdx);
this.renderList();
e.preventDefault();
e.stopPropagation();
}
private onListDoubleClick(e: MouseEvent): void {
const targetIdx = this.getClickedItemIndex(e);
if (targetIdx < 0) {
return;
}
const item = this.model.items[targetIdx];
if (item) {
this.editSetting(item.pattern);
e.preventDefault();
e.stopPropagation();
}
}
private getClickedItemIndex(e: MouseEvent): number {
if (!e.target) {
return -1;
}
const actionbar = DOM.findParentWithClass(<any>e.target, 'monaco-action-bar');
if (actionbar) {
// Don't handle doubleclicks inside the action bar
return -1;
}
const element = DOM.findParentWithClass((<any>e.target), 'setting-exclude-row');
if (!element) {
return -1;
}
const targetIdxStr = element.getAttribute('data-index');
if (!targetIdxStr) {
return -1;
}
const targetIdx = parseInt(targetIdxStr);
return targetIdx;
}
private renderList(): void {
const focused = DOM.isAncestor(document.activeElement, this.listElement);
DOM.clearNode(this.listElement);
this.listDisposables = dispose(this.listDisposables);
const newMode = this.model.items.some(item => !!(item.editing && !item.pattern));
DOM.toggleClass(this.container, 'setting-exclude-new-mode', newMode);
this.model.items
.map((item, i) => this.renderItem(item, i, focused))
.forEach(itemElement => this.listElement.appendChild(itemElement));
const listHeight = 22 * this.model.items.length;
this.listElement.style.height = listHeight + 'px';
}
private createDeleteAction(key: string): IAction {
return <IAction>{
class: 'setting-excludeAction-remove',
enabled: true,
id: 'workbench.action.removeExcludeItem',
tooltip: localize('removeExcludeItem', "Remove Exclude Item"),
run: () => this._onDidChangeExclude.fire({ originalPattern: key, pattern: undefined })
};
}
private createEditAction(key: string): IAction {
return <IAction>{
class: 'setting-excludeAction-edit',
enabled: true,
id: 'workbench.action.editExcludeItem',
tooltip: localize('editExcludeItem', "Edit Exclude Item"),
run: () => {
this.editSetting(key);
}
};
}
private editSetting(key: string): void {
this.model.setEditKey(key);
this.renderList();
}
private renderItem(item: IExcludeViewItem, idx: number, listFocused: boolean): HTMLElement {
return item.editing ?
this.renderEditItem(item) :
this.renderDataItem(item, idx, listFocused);
}
private renderDataItem(item: IExcludeViewItem, idx: number, listFocused: boolean): HTMLElement {
const rowElement = $('.setting-exclude-row');
rowElement.setAttribute('data-index', idx + '');
rowElement.setAttribute('tabindex', item.selected ? '0' : '-1');
DOM.toggleClass(rowElement, 'selected', item.selected);
const actionBar = new ActionBar(rowElement);
this.listDisposables.push(actionBar);
const patternElement = DOM.append(rowElement, $('.setting-exclude-pattern'));
const siblingElement = DOM.append(rowElement, $('.setting-exclude-sibling'));
patternElement.textContent = item.pattern;
siblingElement.textContent = item.sibling ? ('when: ' + item.sibling) : null;
actionBar.push([
this.createEditAction(item.pattern),
this.createDeleteAction(item.pattern)
], { icon: true, label: false });
rowElement.title = item.sibling ?
localize('excludeSiblingHintLabel', "Exclude files matching `{0}`, only when a file matching `{1}` is present", item.pattern, item.sibling) :
localize('excludePatternHintLabel', "Exclude files matching `{0}`", item.pattern);
if (item.selected) {
if (listFocused) {
setTimeout(() => {
rowElement.focus();
}, 10);
}
}
return rowElement;
}
private renderAddButton(): HTMLElement {
const rowElement = $('.setting-exclude-new-row');
const startAddButton = this._register(new Button(rowElement));
startAddButton.label = localize('addPattern', "Add Pattern");
startAddButton.element.classList.add('setting-exclude-addButton');
this._register(attachButtonStyler(startAddButton, this.themeService));
this._register(startAddButton.onDidClick(() => {
this.model.setEditKey('');
this.renderList();
}));
return rowElement;
}
private renderEditItem(item: IExcludeViewItem): HTMLElement {
const rowElement = $('.setting-exclude-edit-row');
const onSubmit = edited => {
this.model.setEditKey(null);
const pattern = patternInput.value.trim();
if (edited && pattern) {
this._onDidChangeExclude.fire({
originalPattern: item.pattern,
pattern,
sibling: siblingInput && siblingInput.value.trim()
});
}
this.renderList();
};
const onKeydown = (e: StandardKeyboardEvent) => {
if (e.equals(KeyCode.Enter)) {
onSubmit(true);
} else if (e.equals(KeyCode.Escape)) {
onSubmit(false);
e.preventDefault();
}
};
const patternInput = new InputBox(rowElement, this.contextViewService, {
placeholder: localize('excludePatternInputPlaceholder', "Exclude Pattern...")
});
patternInput.element.classList.add('setting-exclude-patternInput');
this.listDisposables.push(attachInputBoxStyler(patternInput, this.themeService, {
inputBackground: settingsTextInputBackground,
inputForeground: settingsTextInputForeground,
inputBorder: settingsTextInputBorder
}));
this.listDisposables.push(patternInput);
patternInput.value = item.pattern;
this.listDisposables.push(DOM.addStandardDisposableListener(patternInput.inputElement, DOM.EventType.KEY_DOWN, onKeydown));
let siblingInput: InputBox;
if (item.sibling) {
siblingInput = new InputBox(rowElement, this.contextViewService, {
placeholder: localize('excludeSiblingInputPlaceholder', "When Pattern Is Present...")
});
siblingInput.element.classList.add('setting-exclude-siblingInput');
this.listDisposables.push(siblingInput);
this.listDisposables.push(attachInputBoxStyler(siblingInput, this.themeService, {
inputBackground: settingsTextInputBackground,
inputForeground: settingsTextInputForeground,
inputBorder: settingsTextInputBorder
}));
siblingInput.value = item.sibling;
this.listDisposables.push(DOM.addStandardDisposableListener(siblingInput.inputElement, DOM.EventType.KEY_DOWN, onKeydown));
}
const okButton = this._register(new Button(rowElement));
okButton.label = localize('okButton', "OK");
okButton.element.classList.add('setting-exclude-okButton');
this.listDisposables.push(attachButtonStyler(okButton, this.themeService));
this.listDisposables.push(okButton.onDidClick(() => onSubmit(true)));
const cancelButton = this._register(new Button(rowElement));
cancelButton.label = localize('cancelButton', "Cancel");
cancelButton.element.classList.add('setting-exclude-cancelButton');
this.listDisposables.push(attachButtonStyler(cancelButton, this.themeService));
this.listDisposables.push(cancelButton.onDidClick(() => onSubmit(false)));
this.listDisposables.push(
disposableTimeout(() => {
patternInput.focus();
patternInput.select();
}));
return rowElement;
}
dispose() {
super.dispose();
this.listDisposables = dispose(this.listDisposables);
}
}
export interface IExcludeDataItem {
pattern: string;
sibling?: string;
}
interface IExcludeViewItem extends IExcludeDataItem {
editing?: boolean;
selected?: boolean;
}

View File

@@ -0,0 +1,228 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as DOM from 'vs/base/browser/dom';
import { IListVirtualDelegate } from 'vs/base/browser/ui/list/list';
import { DefaultStyleController, IAccessibilityProvider } from 'vs/base/browser/ui/list/listWidget';
import { IObjectTreeOptions, ObjectTree } from 'vs/base/browser/ui/tree/objectTree';
import { ITreeElement, ITreeNode, ITreeRenderer } from 'vs/base/browser/ui/tree/tree';
import { Iterator } from 'vs/base/common/iterator';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { editorBackground } from 'vs/platform/theme/common/colorRegistry';
import { attachStyler } from 'vs/platform/theme/common/styler';
import { IThemeService } from 'vs/platform/theme/common/themeService';
import { SettingsTreeFilter } from 'vs/workbench/contrib/preferences/browser/settingsTree';
import { ISettingsEditorViewState, SearchResultModel, SettingsTreeElement, SettingsTreeGroupElement, SettingsTreeSettingElement } from 'vs/workbench/contrib/preferences/browser/settingsTreeModels';
import { settingsHeaderForeground } from 'vs/workbench/contrib/preferences/browser/settingsWidgets';
import { localize } from 'vs/nls';
const $ = DOM.$;
export class TOCTreeModel {
private _currentSearchModel: SearchResultModel | null;
private _settingsTreeRoot: SettingsTreeGroupElement;
constructor(private _viewState: ISettingsEditorViewState) {
}
get settingsTreeRoot(): SettingsTreeGroupElement {
return this._settingsTreeRoot;
}
set settingsTreeRoot(value: SettingsTreeGroupElement) {
this._settingsTreeRoot = value;
this.update();
}
get currentSearchModel(): SearchResultModel | null {
return this._currentSearchModel;
}
set currentSearchModel(model: SearchResultModel | null) {
this._currentSearchModel = model;
this.update();
}
get children(): SettingsTreeElement[] {
return this._settingsTreeRoot.children;
}
update(): void {
if (this._settingsTreeRoot) {
this.updateGroupCount(this._settingsTreeRoot);
}
}
private updateGroupCount(group: SettingsTreeGroupElement): void {
group.children.forEach(child => {
if (child instanceof SettingsTreeGroupElement) {
this.updateGroupCount(child);
}
});
const childCount = group.children
.filter(child => child instanceof SettingsTreeGroupElement)
.reduce((acc, cur) => acc + (<SettingsTreeGroupElement>cur).count!, 0);
group.count = childCount + this.getGroupCount(group);
}
private getGroupCount(group: SettingsTreeGroupElement): number {
return group.children.filter(child => {
if (!(child instanceof SettingsTreeSettingElement)) {
return false;
}
if (this._currentSearchModel && !this._currentSearchModel.root.containsSetting(child.setting.key)) {
return false;
}
// Check everything that the SettingsFilter checks except whether it's filtered by a category
return child.matchesScope(this._viewState.settingsTarget) && child.matchesAllTags(this._viewState.tagFilters);
}).length;
}
}
const TOC_ENTRY_TEMPLATE_ID = 'settings.toc.entry';
interface ITOCEntryTemplate {
labelElement: HTMLElement;
countElement: HTMLElement;
}
export class TOCRenderer implements ITreeRenderer<SettingsTreeGroupElement, never, ITOCEntryTemplate> {
templateId = TOC_ENTRY_TEMPLATE_ID;
renderTemplate(container: HTMLElement): ITOCEntryTemplate {
return {
labelElement: DOM.append(container, $('.settings-toc-entry')),
countElement: DOM.append(container, $('.settings-toc-count'))
};
}
renderElement(node: ITreeNode<SettingsTreeGroupElement>, index: number, template: ITOCEntryTemplate): void {
const element = node.element;
const count = element.count;
const label = element.label;
template.labelElement.textContent = label;
if (count) {
template.countElement.textContent = ` (${count})`;
} else {
template.countElement.textContent = '';
}
}
disposeTemplate(templateData: ITOCEntryTemplate): void {
}
}
class TOCTreeDelegate implements IListVirtualDelegate<SettingsTreeElement> {
getTemplateId(element: SettingsTreeElement): string {
return TOC_ENTRY_TEMPLATE_ID;
}
getHeight(element: SettingsTreeElement): number {
return 22;
}
}
export function createTOCIterator(model: TOCTreeModel | SettingsTreeGroupElement, tree: TOCTree): Iterator<ITreeElement<SettingsTreeGroupElement>> {
const groupChildren = <SettingsTreeGroupElement[]>model.children.filter(c => c instanceof SettingsTreeGroupElement);
const groupsIt = Iterator.fromArray(groupChildren);
return Iterator.map(groupsIt, g => {
let nodeExists = true;
try { tree.getNode(g); } catch (e) { nodeExists = false; }
const hasGroupChildren = g.children.some(c => c instanceof SettingsTreeGroupElement);
return {
element: g,
collapsed: nodeExists ? undefined : true,
collapsible: hasGroupChildren,
children: g instanceof SettingsTreeGroupElement ?
createTOCIterator(g, tree) :
undefined
};
});
}
class SettingsAccessibilityProvider implements IAccessibilityProvider<SettingsTreeGroupElement> {
getAriaLabel(element: SettingsTreeElement): string {
if (!element) {
return '';
}
if (element instanceof SettingsTreeGroupElement) {
return localize('groupRowAriaLabel', "{0}, group", element.label);
}
return '';
}
getAriaLevel(element: SettingsTreeGroupElement): number {
let i = 1;
while (element instanceof SettingsTreeGroupElement && element.parent) {
i++;
element = element.parent;
}
return i;
}
}
export class TOCTree extends ObjectTree<SettingsTreeGroupElement> {
constructor(
container: HTMLElement,
viewState: ISettingsEditorViewState,
@IThemeService themeService: IThemeService,
@IInstantiationService instantiationService: IInstantiationService
) {
// test open mode
const treeClass = 'settings-toc-tree';
const filter = instantiationService.createInstance(SettingsTreeFilter, viewState);
const options: IObjectTreeOptions<SettingsTreeGroupElement> = {
filter,
multipleSelectionSupport: false,
identityProvider: {
getId(e) {
return e.id;
}
},
styleController: new DefaultStyleController(DOM.createStyleSheet(container), treeClass),
accessibilityProvider: instantiationService.createInstance(SettingsAccessibilityProvider)
};
super(container,
new TOCTreeDelegate(),
[new TOCRenderer()],
options);
this.getHTMLElement().classList.add(treeClass);
this.disposables.push(attachStyler(themeService, {
listActiveSelectionBackground: editorBackground,
listActiveSelectionForeground: settingsHeaderForeground,
listFocusAndSelectionBackground: editorBackground,
listFocusAndSelectionForeground: settingsHeaderForeground,
listFocusBackground: editorBackground,
listFocusForeground: settingsHeaderForeground,
listHoverForeground: settingsHeaderForeground,
listHoverBackground: editorBackground,
listInactiveSelectionBackground: editorBackground,
listInactiveSelectionForeground: settingsHeaderForeground,
listInactiveFocusBackground: editorBackground,
listInactiveFocusOutline: editorBackground
}, colors => {
this.style(colors);
}));
}
}