SQL Operations Studio Public Preview 1 (0.23) release source code

This commit is contained in:
Karl Burtram
2017-11-09 14:30:27 -08:00
parent b88ecb8d93
commit 3cdac41339
8829 changed files with 759707 additions and 286 deletions

View File

@@ -0,0 +1,298 @@
/*---------------------------------------------------------------------------------------------
* 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 { TPromise } from 'vs/base/common/winjs.base';
import { Disposable } 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 { InputBox, IInputOptions } from 'vs/base/browser/ui/inputbox/inputBox';
import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent';
import { FastDomNode, createFastDomNode } from 'vs/base/browser/fastDomNode';
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
import { Dimension } from 'vs/base/browser/builder';
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';
class KeybindingInputWidget extends Widget {
private readonly inputBox: InputBox;
private _acceptChords: boolean;
private _firstPart: ResolvedKeybinding;
private _chordPart: ResolvedKeybinding;
private _inputValue: string;
private _onKeybinding = this._register(new Emitter<[ResolvedKeybinding, ResolvedKeybinding]>());
public readonly onKeybinding: Event<[ResolvedKeybinding, ResolvedKeybinding]> = this._onKeybinding.event;
private _onEnter = this._register(new Emitter<void>());
public readonly onEnter: Event<void> = this._onEnter.event;
private _onEscape = this._register(new Emitter<void>());
public readonly onEscape: Event<void> = this._onEscape.event;
private _onBlur = this._register(new Emitter<void>());
public readonly onBlur: Event<void> = this._onBlur.event;
constructor(parent: HTMLElement, private options: IInputOptions,
@IContextViewService private contextViewService: IContextViewService,
@IKeybindingService private keybindingService: IKeybindingService,
@IThemeService themeService: IThemeService
) {
super();
this.inputBox = this._register(new InputBox(parent, this.contextViewService, this.options));
this._register(attachInputBoxStyler(this.inputBox, themeService));
this.onkeydown(this.inputBox.inputElement, e => this._onKeyDown(e));
this.onblur(this.inputBox.inputElement, (e) => this._onBlur.fire());
this.oninput(this.inputBox.inputElement, (e) => {
// Prevent other characters from showing up
this.setInputValue(this._inputValue);
});
this._acceptChords = true;
this._firstPart = null;
this._chordPart = null;
}
public setInputValue(value: string): void {
this._inputValue = value;
this.inputBox.value = this._inputValue;
}
public focus(): void {
this.inputBox.focus();
}
public reset() {
this._firstPart = null;
this._chordPart = null;
}
public setAcceptChords(acceptChords: boolean) {
this._acceptChords = acceptChords;
this._chordPart = null;
}
private _onKeyDown(keyboardEvent: IKeyboardEvent): void {
keyboardEvent.preventDefault();
keyboardEvent.stopPropagation();
if (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]}`;
if (this._acceptChords) {
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;
}
} else {
this._firstPart = keybinding;
}
let value = '';
if (this._firstPart) {
value = this._firstPart.getUserSettingsLabel();
}
if (this._chordPart) {
value = value + ' ' + this._chordPart.getUserSettingsLabel();
}
this.setInputValue(value);
this.inputBox.inputElement.title = info;
this._onKeybinding.fire([this._firstPart, this._chordPart]);
}
}
export class DefineKeybindingWidget extends Widget {
private static WIDTH = 400;
private static HEIGHT = 90;
private _domNode: FastDomNode<HTMLElement>;
private _keybindingInputWidget: KeybindingInputWidget;
private _outputNode: HTMLElement;
private _firstPart: ResolvedKeybinding = null;
private _chordPart: ResolvedKeybinding = null;
private _isVisible: boolean = false;
private _onHide = this._register(new Emitter<void>());
constructor(
parent: HTMLElement,
@IKeybindingService private keybindingService: IKeybindingService,
@IInstantiationService private instantiationService: IInstantiationService,
@IThemeService private themeService: IThemeService
) {
super();
this.create();
if (parent) {
dom.append(parent, this._domNode.domNode);
}
}
get domNode(): HTMLElement {
return this._domNode.domNode;
}
define(): TPromise<string> {
this._keybindingInputWidget.reset();
return new TPromise<string>((c, e) => {
if (!this._isVisible) {
this._isVisible = true;
this._domNode.setDisplay('block');
this._firstPart = null;
this._chordPart = null;
this._keybindingInputWidget.setInputValue('');
dom.clearNode(this._outputNode);
this._keybindingInputWidget.focus();
}
const disposable = this._onHide.event(() => {
if (this._firstPart) {
let r = this._firstPart.getUserSettingsLabel();
if (this._chordPart) {
r = r + ' ' + this._chordPart.getUserSettingsLabel();
}
c(r);
} else {
c(null);
}
disposable.dispose();
});
});
}
layout(layout: Dimension): void {
let top = Math.round((layout.height - DefineKeybindingWidget.HEIGHT) / 2);
this._domNode.setTop(top);
let left = Math.round((layout.width - DefineKeybindingWidget.WIDTH) / 2);
this._domNode.setLeft(left);
}
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);
dom.append(this._domNode.domNode, dom.$('.message', null, nls.localize('defineKeybinding.initial', "Press desired key combination and ENTER. ESCAPE to cancel.")));
this._register(attachStylerCallback(this.themeService, { editorWidgetBackground, widgetShadow }, colors => {
this._domNode.domNode.style.backgroundColor = colors.editorWidgetBackground;
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(KeybindingInputWidget, this._domNode.domNode, {}));
this._register(this._keybindingInputWidget.onKeybinding(keybinding => this.printKeybinding(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'));
}
private printKeybinding(keybinding: [ResolvedKeybinding, ResolvedKeybinding]): void {
const [firstPart, chordPart] = keybinding;
this._firstPart = firstPart;
this._chordPart = chordPart;
dom.clearNode(this._outputNode);
new KeybindingLabel(this._outputNode, OS).set(this._firstPart, null);
if (this._chordPart) {
this._outputNode.appendChild(document.createTextNode(nls.localize('defineKeybinding.chordsTo', "chord to")));
new KeybindingLabel(this._outputNode, OS).set(this._chordPart, null);
}
}
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 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);
}
public getId(): string {
return DefineKeybindingOverlayWidget.ID;
}
public getDomNode(): HTMLElement {
return this._widget.domNode;
}
public getPosition(): IOverlayWidgetPosition {
return {
preference: null
};
}
public dispose(): void {
this._editor.removeOverlayWidget(this);
super.dispose();
}
public start(): TPromise<string> {
this._editor.revealPositionInCenterIfOutsideViewport(this._editor.getPosition(), ScrollType.Smooth);
const layoutInfo = this._editor.getLayoutInfo();
this._widget.layout(new Dimension(layoutInfo.width, layoutInfo.height));
return this._widget.define();
}
}

View File

@@ -0,0 +1,808 @@
/*---------------------------------------------------------------------------------------------
* 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/keybindingsEditor';
import { localize } from 'vs/nls';
import { TPromise } from 'vs/base/common/winjs.base';
import { Delayer } from 'vs/base/common/async';
import * as DOM from 'vs/base/browser/dom';
import { OS } from 'vs/base/common/platform';
import { Checkbox } from 'vs/base/browser/ui/checkbox/checkbox';
import { Builder, Dimension } from 'vs/base/browser/builder';
import { HighlightedLabel } from 'vs/base/browser/ui/highlightedlabel/highlightedLabel';
import { KeybindingLabel } from 'vs/base/browser/ui/keybindingLabel/keybindingLabel';
import { IAction } from 'vs/base/common/actions';
import { ActionBar, Separator } from 'vs/base/browser/ui/actionbar/actionbar';
import { BaseEditor } from 'vs/workbench/browser/parts/editor/baseEditor';
import { EditorInput } from 'vs/workbench/common/editor';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService';
import { KeybindingsEditorModel, IKeybindingItemEntry, IListEntry, KEYBINDING_ENTRY_TEMPLATE_ID, KEYBINDING_HEADER_TEMPLATE_ID } from 'vs/workbench/parts/preferences/common/keybindingsEditorModel';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IKeybindingService, IUserFriendlyKeybinding } from 'vs/platform/keybinding/common/keybinding';
import { SearchWidget } from 'vs/workbench/parts/preferences/browser/preferencesWidgets';
import { DefineKeybindingWidget } from 'vs/workbench/parts/preferences/browser/keybindingWidgets';
import {
IPreferencesService, IKeybindingsEditor, CONTEXT_KEYBINDING_FOCUS, CONTEXT_KEYBINDINGS_EDITOR, CONTEXT_KEYBINDINGS_SEARCH_FOCUS, KEYBINDINGS_EDITOR_COMMAND_REMOVE, KEYBINDINGS_EDITOR_COMMAND_COPY,
KEYBINDINGS_EDITOR_COMMAND_RESET, KEYBINDINGS_EDITOR_COMMAND_DEFINE, KEYBINDINGS_EDITOR_COMMAND_SHOW_CONFLICTS
} from 'vs/workbench/parts/preferences/common/preferences';
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
import { IKeybindingEditingService } from 'vs/workbench/services/keybinding/common/keybindingEditing';
import { IListService } from 'vs/platform/list/browser/listService';
import { List } from 'vs/base/browser/ui/list/listWidget';
import { IDelegate, IRenderer, IListContextMenuEvent, IListEvent } from 'vs/base/browser/ui/list/list';
import { IThemeService, registerThemingParticipant, ITheme, ICssStyleCollector } from 'vs/platform/theme/common/themeService';
import { IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey';
import { IChoiceService, IMessageService, Severity } from 'vs/platform/message/common/message';
import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent';
import { KeyCode, ResolvedKeybinding } from 'vs/base/common/keyCodes';
import { attachListStyler } from 'vs/platform/theme/common/styler';
import { listHighlightForeground } from 'vs/platform/theme/common/colorRegistry';
import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService';
let $ = DOM.$;
export class KeybindingsEditorInput extends EditorInput {
public static ID: string = 'workbench.input.keybindings';
public readonly keybindingsModel: KeybindingsEditorModel;
constructor( @IInstantiationService private instantiationService: IInstantiationService) {
super();
this.keybindingsModel = instantiationService.createInstance(KeybindingsEditorModel, OS);
}
getTypeId(): string {
return KeybindingsEditorInput.ID;
}
getName(): string {
return localize('keybindingsInputName', "Keyboard Shortcuts");
}
resolve(refresh?: boolean): TPromise<KeybindingsEditorModel> {
return TPromise.as(this.keybindingsModel);
}
matches(otherInput: any): boolean {
return otherInput instanceof KeybindingsEditorInput;
}
}
export class KeybindingsEditor extends BaseEditor implements IKeybindingsEditor {
public static ID: string = 'workbench.editor.keybindings';
private keybindingsEditorModel: KeybindingsEditorModel;
private headerContainer: HTMLElement;
private searchWidget: SearchWidget;
private overlayContainer: HTMLElement;
private defineKeybindingWidget: DefineKeybindingWidget;
private keybindingsListContainer: HTMLElement;
private unAssignedKeybindingItemToRevealAndFocus: IKeybindingItemEntry;
private listEntries: IListEntry[];
private keybindingsList: List<IListEntry>;
private dimension: Dimension;
private delayedFiltering: Delayer<void>;
private latestEmptyFilters: string[] = [];
private delayedFilterLogging: Delayer<void>;
private keybindingsEditorContextKey: IContextKey<boolean>;
private keybindingFocusContextKey: IContextKey<boolean>;
private searchFocusContextKey: IContextKey<boolean>;
private sortByPrecedence: Checkbox;
constructor(
@ITelemetryService telemetryService: ITelemetryService,
@IThemeService themeService: IThemeService,
@IKeybindingService private keybindingsService: IKeybindingService,
@IContextMenuService private contextMenuService: IContextMenuService,
@IPreferencesService private preferencesService: IPreferencesService,
@IKeybindingEditingService private keybindingEditingService: IKeybindingEditingService,
@IListService private listService: IListService,
@IContextKeyService private contextKeyService: IContextKeyService,
@IChoiceService private choiceService: IChoiceService,
@IMessageService private messageService: IMessageService,
@IClipboardService private clipboardService: IClipboardService,
@IInstantiationService private instantiationService: IInstantiationService,
@IWorkbenchEditorService private editorService: IWorkbenchEditorService
) {
super(KeybindingsEditor.ID, telemetryService, themeService);
this.delayedFiltering = new Delayer<void>(300);
this._register(keybindingsService.onDidUpdateKeybindings(() => this.render()));
this.keybindingsEditorContextKey = CONTEXT_KEYBINDINGS_EDITOR.bindTo(this.contextKeyService);
this.searchFocusContextKey = CONTEXT_KEYBINDINGS_SEARCH_FOCUS.bindTo(this.contextKeyService);
this.keybindingFocusContextKey = CONTEXT_KEYBINDING_FOCUS.bindTo(this.contextKeyService);
this.delayedFilterLogging = new Delayer<void>(1000);
}
createEditor(parent: Builder): void {
const parentElement = parent.getHTMLElement();
const keybindingsEditorElement = DOM.append(parentElement, $('div', { class: 'keybindings-editor' }));
this.createOverlayContainer(keybindingsEditorElement);
this.createHeader(keybindingsEditorElement);
this.createBody(keybindingsEditorElement);
const focusTracker = this._register(DOM.trackFocus(parentElement));
this._register(focusTracker.addFocusListener(() => this.keybindingsEditorContextKey.set(true)));
this._register(focusTracker.addBlurListener(() => this.keybindingsEditorContextKey.reset()));
}
setInput(input: KeybindingsEditorInput): TPromise<void> {
const oldInput = this.input;
return super.setInput(input)
.then(() => {
if (!input.matches(oldInput)) {
this.render();
}
});
}
clearInput(): void {
super.clearInput();
this.searchWidget.clear();
this.keybindingsEditorContextKey.reset();
this.keybindingFocusContextKey.reset();
}
layout(dimension: Dimension): void {
this.dimension = dimension;
this.searchWidget.layout(dimension);
this.overlayContainer.style.width = dimension.width + 'px';
this.overlayContainer.style.height = dimension.height + 'px';
this.defineKeybindingWidget.layout(this.dimension);
this.layoutKebindingsList();
}
focus(): void {
const activeKeybindingEntry = this.activeKeybindingEntry;
if (activeKeybindingEntry) {
this.selectEntry(activeKeybindingEntry);
} else {
this.searchWidget.focus();
}
}
get activeKeybindingEntry(): IKeybindingItemEntry {
const focusedElement = this.keybindingsList.getFocusedElements()[0];
return focusedElement && focusedElement.templateId === KEYBINDING_ENTRY_TEMPLATE_ID ? <IKeybindingItemEntry>focusedElement : null;
}
defineKeybinding(keybindingEntry: IKeybindingItemEntry): TPromise<any> {
this.selectEntry(keybindingEntry);
this.showOverlayContainer();
return this.defineKeybindingWidget.define().then(key => {
this.reportKeybindingAction(KEYBINDINGS_EDITOR_COMMAND_DEFINE, keybindingEntry.keybindingItem.command, key);
if (key) {
return this.keybindingEditingService.editKeybinding(key, keybindingEntry.keybindingItem.keybindingItem)
.then(() => {
if (!keybindingEntry.keybindingItem.keybinding) { // reveal only if keybinding was added to unassinged. Because the entry will be placed in different position after rendering
this.unAssignedKeybindingItemToRevealAndFocus = keybindingEntry;
}
});
}
return null;
}).then(() => {
this.hideOverlayContainer();
this.selectEntry(keybindingEntry);
}, error => {
this.hideOverlayContainer();
this.onKeybindingEditingError(error);
this.selectEntry(keybindingEntry);
return error;
});
}
removeKeybinding(keybindingEntry: IKeybindingItemEntry): TPromise<any> {
this.selectEntry(keybindingEntry);
if (keybindingEntry.keybindingItem.keybinding) { // This should be a pre-condition
this.reportKeybindingAction(KEYBINDINGS_EDITOR_COMMAND_REMOVE, keybindingEntry.keybindingItem.command, keybindingEntry.keybindingItem.keybinding);
return this.keybindingEditingService.removeKeybinding(keybindingEntry.keybindingItem.keybindingItem)
.then(() => this.focus(),
error => {
this.onKeybindingEditingError(error);
this.selectEntry(keybindingEntry);
});
}
return TPromise.as(null);
}
resetKeybinding(keybindingEntry: IKeybindingItemEntry): TPromise<any> {
this.selectEntry(keybindingEntry);
this.reportKeybindingAction(KEYBINDINGS_EDITOR_COMMAND_RESET, keybindingEntry.keybindingItem.command, keybindingEntry.keybindingItem.keybinding);
return this.keybindingEditingService.resetKeybinding(keybindingEntry.keybindingItem.keybindingItem)
.then(() => {
if (!keybindingEntry.keybindingItem.keybinding) { // reveal only if keybinding was added to unassinged. Because the entry will be placed in different position after rendering
this.unAssignedKeybindingItemToRevealAndFocus = keybindingEntry;
}
this.selectEntry(keybindingEntry);
},
error => {
this.onKeybindingEditingError(error);
this.selectEntry(keybindingEntry);
});
}
copyKeybinding(keybinding: IKeybindingItemEntry): TPromise<any> {
this.selectEntry(keybinding);
this.reportKeybindingAction(KEYBINDINGS_EDITOR_COMMAND_COPY, keybinding.keybindingItem.command, keybinding.keybindingItem.keybinding);
const userFriendlyKeybinding: IUserFriendlyKeybinding = {
command: keybinding.keybindingItem.command,
key: keybinding.keybindingItem.keybinding ? keybinding.keybindingItem.keybinding.getUserSettingsLabel() : ''
};
if (keybinding.keybindingItem.when) {
userFriendlyKeybinding.when = keybinding.keybindingItem.when;
}
this.clipboardService.writeText(JSON.stringify(userFriendlyKeybinding, null, ' '));
return TPromise.as(null);
}
search(filter: string): void {
this.searchWidget.focus();
}
showConflicts(keybindingEntry: IKeybindingItemEntry): TPromise<any> {
const value = `"${keybindingEntry.keybindingItem.keybinding.getAriaLabel()}"`;
if (value !== this.searchWidget.getValue()) {
this.searchWidget.setValue(value);
}
return TPromise.as(null);
}
private createOverlayContainer(parent: HTMLElement): void {
this.overlayContainer = DOM.append(parent, $('.overlay-container'));
this.overlayContainer.style.position = 'absolute';
this.overlayContainer.style.zIndex = '10';
this.defineKeybindingWidget = this._register(this.instantiationService.createInstance(DefineKeybindingWidget, this.overlayContainer));
this.hideOverlayContainer();
}
private showOverlayContainer() {
this.overlayContainer.style.display = 'block';
}
private hideOverlayContainer() {
this.overlayContainer.style.display = 'none';
}
private createHeader(parent: HTMLElement): void {
this.headerContainer = DOM.append(parent, $('.keybindings-header'));
const searchContainer = DOM.append(this.headerContainer, $('.search-container'));
this.searchWidget = this._register(this.instantiationService.createInstance(SearchWidget, searchContainer, {
ariaLabel: localize('SearchKeybindings.AriaLabel', "Search keybindings"),
placeholder: localize('SearchKeybindings.Placeholder', "Search keybindings"),
focusKey: this.searchFocusContextKey
}));
this._register(this.searchWidget.onDidChange(searchValue => this.delayedFiltering.trigger(() => this.filterKeybindings())));
this.sortByPrecedence = this._register(new Checkbox({
actionClassName: 'sort-by-precedence',
isChecked: false,
onChange: () => this.renderKeybindingsEntries(false),
title: localize('sortByPrecedene', "Sort by Precedence")
}));
searchContainer.appendChild(this.sortByPrecedence.domNode);
this.createOpenKeybindingsElement(this.headerContainer);
}
private createOpenKeybindingsElement(parent: HTMLElement): void {
const openKeybindingsContainer = DOM.append(parent, $('.open-keybindings-container'));
DOM.append(openKeybindingsContainer, $('', null, localize('header-message', "For advanced customizations open and edit")));
const fileElement = DOM.append(openKeybindingsContainer, $('.file-name', null, localize('keybindings-file-name', "keybindings.json")));
fileElement.tabIndex = 0;
this._register(DOM.addDisposableListener(fileElement, DOM.EventType.CLICK, () => this.preferencesService.openGlobalKeybindingSettings(true)));
this._register(DOM.addDisposableListener(fileElement, DOM.EventType.KEY_UP, e => {
let keyboardEvent = new StandardKeyboardEvent(e);
switch (keyboardEvent.keyCode) {
case KeyCode.Enter:
this.preferencesService.openGlobalKeybindingSettings(true);
keyboardEvent.preventDefault();
keyboardEvent.stopPropagation();
return;
}
}));
}
private createBody(parent: HTMLElement): void {
const bodyContainer = DOM.append(parent, $('.keybindings-body'));
this.createList(bodyContainer);
}
private createList(parent: HTMLElement): void {
this.keybindingsListContainer = DOM.append(parent, $('.keybindings-list-container'));
this.keybindingsList = this._register(new List<IListEntry>(this.keybindingsListContainer, new Delegate(), [new KeybindingHeaderRenderer(), new KeybindingItemRenderer(this, this.keybindingsService)],
{ identityProvider: e => e.id, keyboardSupport: false, mouseSupport: true, ariaLabel: localize('keybindingsLabel', "Keybindings") }));
this._register(this.keybindingsList.onContextMenu(e => this.onContextMenu(e)));
this._register(this.keybindingsList.onFocusChange(e => this.onFocusChange(e)));
this._register(this.keybindingsList.onDOMFocus(() => {
DOM.addClass(this.keybindingsList.getHTMLElement(), 'focused');
}));
this._register(this.keybindingsList.onDOMBlur(() => {
DOM.removeClass(this.keybindingsList.getHTMLElement(), 'focused');
this.keybindingFocusContextKey.reset();
}));
this._register(attachListStyler(this.keybindingsList, this.themeService));
this._register(this.listService.register(this.keybindingsList));
}
private render(): TPromise<any> {
if (this.input) {
return this.input.resolve()
.then((keybindingsModel: KeybindingsEditorModel) => this.keybindingsEditorModel = keybindingsModel)
.then(() => this.keybindingsEditorModel.resolve())
.then(() => this.renderKeybindingsEntries(false));
}
return TPromise.as(null);
}
private filterKeybindings(): void {
this.renderKeybindingsEntries(this.searchWidget.hasFocus());
this.delayedFilterLogging.trigger(() => this.reportFilteringUsed(this.searchWidget.getValue()));
}
private renderKeybindingsEntries(reset: boolean): void {
if (this.keybindingsEditorModel) {
const filter = this.searchWidget.getValue();
const keybindingsEntries: IKeybindingItemEntry[] = this.keybindingsEditorModel.fetch(filter, this.sortByPrecedence.checked);
if (keybindingsEntries.length === 0) {
this.latestEmptyFilters.push(filter);
}
const currentSelectedIndex = this.keybindingsList.getSelection()[0];
this.listEntries = [{ id: 'keybinding-header-entry', templateId: KEYBINDING_HEADER_TEMPLATE_ID }, ...keybindingsEntries];
this.keybindingsList.splice(0, this.keybindingsList.length, this.listEntries);
this.layoutKebindingsList();
if (reset) {
this.keybindingsList.setSelection([]);
this.keybindingsList.setFocus([]);
} else {
if (this.unAssignedKeybindingItemToRevealAndFocus) {
const index = this.getNewIndexOfUnassignedKeybinding(this.unAssignedKeybindingItemToRevealAndFocus);
if (index !== -1) {
this.keybindingsList.reveal(index, 0.2);
this.selectEntry(index);
}
this.unAssignedKeybindingItemToRevealAndFocus = null;
} else if (currentSelectedIndex !== -1 && currentSelectedIndex < this.listEntries.length) {
this.selectEntry(currentSelectedIndex);
} else if (this.editorService.getActiveEditor() === this) {
this.focus();
}
}
}
}
private layoutKebindingsList(): void {
const listHeight = this.dimension.height - (DOM.getDomNodePagePosition(this.headerContainer).height + 12 /*padding*/);
this.keybindingsListContainer.style.height = `${listHeight}px`;
this.keybindingsList.layout(listHeight);
}
private getIndexOf(listEntry: IListEntry): number {
const index = this.listEntries.indexOf(listEntry);
if (index === -1) {
for (let i = 0; i < this.listEntries.length; i++) {
if (this.listEntries[i].id === listEntry.id) {
return i;
}
}
}
return index;
}
private getNewIndexOfUnassignedKeybinding(unassignedKeybinding: IKeybindingItemEntry): number {
for (let index = 0; index < this.listEntries.length; index++) {
const entry = this.listEntries[index];
if (entry.templateId === KEYBINDING_ENTRY_TEMPLATE_ID) {
const keybindingItemEntry = (<IKeybindingItemEntry>entry);
if (keybindingItemEntry.keybindingItem.command === unassignedKeybinding.keybindingItem.command) {
return index;
}
}
}
return -1;
}
private selectEntry(keybindingItemEntry: IKeybindingItemEntry | number): void {
const index = typeof keybindingItemEntry === 'number' ? keybindingItemEntry : this.getIndexOf(keybindingItemEntry);
if (index !== -1) {
this.keybindingsList.getHTMLElement().focus();
this.keybindingsList.setFocus([index]);
this.keybindingsList.setSelection([index]);
}
}
focusKeybindings(): void {
this.keybindingsList.getHTMLElement().focus();
const currentFocusIndices = this.keybindingsList.getFocus();
this.keybindingsList.setFocus([currentFocusIndices.length ? currentFocusIndices[0] : 0]);
}
private onContextMenu(e: IListContextMenuEvent<IListEntry>): void {
if (e.element.templateId === KEYBINDING_ENTRY_TEMPLATE_ID) {
this.selectEntry(<IKeybindingItemEntry>e.element);
this.contextMenuService.showContextMenu({
getAnchor: () => e.anchor,
getActions: () => TPromise.as([
this.createCopyAction(<IKeybindingItemEntry>e.element),
new Separator(),
this.createDefineAction(<IKeybindingItemEntry>e.element),
this.createRemoveAction(<IKeybindingItemEntry>e.element),
this.createResetAction(<IKeybindingItemEntry>e.element),
new Separator(),
this.createShowConflictsAction(<IKeybindingItemEntry>e.element)])
});
}
}
private onFocusChange(e: IListEvent<IListEntry>): void {
this.keybindingFocusContextKey.reset();
const element = e.elements[0];
if (!element) {
return;
}
if (element.templateId === KEYBINDING_HEADER_TEMPLATE_ID) {
this.keybindingsList.focusNext();
return;
}
if (element.templateId === KEYBINDING_ENTRY_TEMPLATE_ID) {
this.keybindingFocusContextKey.set(true);
}
}
private createDefineAction(keybindingItemEntry: IKeybindingItemEntry): IAction {
return <IAction>{
label: keybindingItemEntry.keybindingItem.keybinding ? localize('changeLabel', "Change Keybinding") : localize('addLabel', "Add Keybinding"),
enabled: true,
id: KEYBINDINGS_EDITOR_COMMAND_DEFINE,
run: () => this.defineKeybinding(keybindingItemEntry)
};
}
private createRemoveAction(keybindingItem: IKeybindingItemEntry): IAction {
return <IAction>{
label: localize('removeLabel', "Remove Keybinding"),
enabled: !!keybindingItem.keybindingItem.keybinding,
id: KEYBINDINGS_EDITOR_COMMAND_REMOVE,
run: () => this.removeKeybinding(keybindingItem)
};
}
private createResetAction(keybindingItem: IKeybindingItemEntry): IAction {
return <IAction>{
label: localize('resetLabel', "Reset Keybinding"),
enabled: !keybindingItem.keybindingItem.keybindingItem.isDefault,
id: KEYBINDINGS_EDITOR_COMMAND_RESET,
run: () => this.resetKeybinding(keybindingItem)
};
}
private createShowConflictsAction(keybindingItem: IKeybindingItemEntry): IAction {
return <IAction>{
label: localize('showConflictsLabel', "Show Conflicts"),
enabled: !!keybindingItem.keybindingItem.keybinding,
id: KEYBINDINGS_EDITOR_COMMAND_SHOW_CONFLICTS,
run: () => this.showConflicts(keybindingItem)
};
}
private createCopyAction(keybindingItem: IKeybindingItemEntry): IAction {
return <IAction>{
label: localize('copyLabel', "Copy"),
enabled: true,
id: KEYBINDINGS_EDITOR_COMMAND_COPY,
run: () => this.copyKeybinding(keybindingItem)
};
}
private reportFilteringUsed(filter: string): void {
if (filter) {
let data = {
filter,
emptyFilters: this.getLatestEmptyFiltersForTelemetry()
};
this.latestEmptyFilters = [];
this.telemetryService.publicLog('keybindings.filter', data);
}
}
/**
* Put a rough limit on the size of the telemetry data, since otherwise it could be an unbounded large amount
* of data. 8192 is the max size of a property value. This is rough since that probably includes ""s, etc.
*/
private getLatestEmptyFiltersForTelemetry(): string[] {
let cumulativeSize = 0;
return this.latestEmptyFilters.filter(filterText => (cumulativeSize += filterText.length) <= 8192);
}
private reportKeybindingAction(action: string, command: string, keybinding: ResolvedKeybinding | string): void {
this.telemetryService.publicLog(action, { command, keybinding: keybinding ? (typeof keybinding === 'string' ? keybinding : keybinding.getUserSettingsLabel()) : '' });
}
private onKeybindingEditingError(error: any): void {
this.messageService.show(Severity.Error, typeof error === 'string' ? error : localize('error', "Error '{0}' while editing keybinding. Please open 'keybindings.json' file and check.", `${error}`));
}
}
class Delegate implements IDelegate<IListEntry> {
getHeight(element: IListEntry) {
if (element.templateId === KEYBINDING_ENTRY_TEMPLATE_ID) {
const commandIdMatched = (<IKeybindingItemEntry>element).keybindingItem.commandLabel && (<IKeybindingItemEntry>element).commandIdMatches;
const commandDefaultLabelMatched = !!(<IKeybindingItemEntry>element).commandDefaultLabelMatches;
if (commandIdMatched && commandDefaultLabelMatched) {
return 60;
}
if (commandIdMatched || commandDefaultLabelMatched) {
return 40;
}
}
if (element.templateId === KEYBINDING_HEADER_TEMPLATE_ID) {
return 30;
}
return 24;
}
getTemplateId(element: IListEntry) {
return element.templateId;
}
}
interface KeybindingItemTemplate {
parent: HTMLElement;
actions: ActionsColumn;
command: CommandColumn;
keybinding: KeybindingColumn;
source: SourceColumn;
when: WhenColumn;
}
class KeybindingHeaderRenderer implements IRenderer<IListEntry, any> {
get templateId(): string { return KEYBINDING_HEADER_TEMPLATE_ID; }
constructor() { }
renderTemplate(container: HTMLElement): any {
DOM.addClass(container, 'keybindings-list-header');
DOM.append(container,
$('.header.actions'),
$('.header.command', null, localize('command', "Command")),
$('.header.keybinding', null, localize('keybinding', "Keybinding")),
$('.header.source', null, localize('source', "Source")),
$('.header.when', null, localize('when', "When")));
return {};
}
renderElement(entry: IListEntry, index: number, template: any): void {
}
disposeTemplate(template: any): void {
}
}
class KeybindingItemRenderer implements IRenderer<IKeybindingItemEntry, KeybindingItemTemplate> {
get templateId(): string { return KEYBINDING_ENTRY_TEMPLATE_ID; }
constructor(private keybindingsEditor: IKeybindingsEditor, private keybindingsService: IKeybindingService) { }
renderTemplate(container: HTMLElement): KeybindingItemTemplate {
DOM.addClass(container, 'keybinding-item');
const actions = new ActionsColumn(container, this.keybindingsEditor, this.keybindingsService);
const command = new CommandColumn(container, this.keybindingsEditor);
const keybinding = new KeybindingColumn(container, this.keybindingsEditor);
const source = new SourceColumn(container, this.keybindingsEditor);
const when = new WhenColumn(container, this.keybindingsEditor);
container.setAttribute('aria-labelledby', [command.id, keybinding.id, source.id, when.id].join(' '));
return {
parent: container,
actions,
command,
keybinding,
source,
when
};
}
renderElement(keybindingEntry: IKeybindingItemEntry, index: number, template: KeybindingItemTemplate): void {
DOM.toggleClass(template.parent, 'even', index % 2 === 0);
template.actions.render(keybindingEntry);
template.command.render(keybindingEntry);
template.keybinding.render(keybindingEntry);
template.source.render(keybindingEntry);
template.when.render(keybindingEntry);
}
disposeTemplate(template: KeybindingItemTemplate): void {
}
}
abstract class Column {
static COUNTER = 0;
protected element: HTMLElement;
readonly id: string;
constructor(protected parent: HTMLElement, protected keybindingsEditor: IKeybindingsEditor) {
this.element = this.create(parent);
this.id = this.element.getAttribute('id');
}
abstract create(parent: HTMLElement): HTMLElement;
}
class ActionsColumn extends Column {
private actionBar: ActionBar;
constructor(parent: HTMLElement, keybindingsEditor: IKeybindingsEditor, private keybindingsService: IKeybindingService) {
super(parent, keybindingsEditor);
}
create(parent: HTMLElement): HTMLElement {
const actionsContainer = DOM.append(parent, $('.column.actions', { id: 'actions_' + ++Column.COUNTER }));
this.actionBar = new ActionBar(actionsContainer, { animated: false });
return actionsContainer;
}
render(keybindingItemEntry: IKeybindingItemEntry): void {
this.actionBar.clear();
const actions = [];
if (keybindingItemEntry.keybindingItem.keybinding) {
actions.push(this.createEditAction(keybindingItemEntry));
} else {
actions.push(this.createAddAction(keybindingItemEntry));
}
this.actionBar.push(actions, { icon: true });
}
private createEditAction(keybindingItemEntry: IKeybindingItemEntry): IAction {
const keybinding = this.keybindingsService.lookupKeybinding(KEYBINDINGS_EDITOR_COMMAND_DEFINE);
return <IAction>{
class: 'edit',
enabled: true,
id: 'editKeybinding',
tooltip: keybinding ? localize('editKeybindingLabelWithKey', "Change Keybinding {0}", `(${keybinding.getLabel()})`) : localize('editKeybindingLabel', "Change Keybinding"),
run: () => this.keybindingsEditor.defineKeybinding(keybindingItemEntry)
};
}
private createAddAction(keybindingItemEntry: IKeybindingItemEntry): IAction {
const keybinding = this.keybindingsService.lookupKeybinding(KEYBINDINGS_EDITOR_COMMAND_DEFINE);
return <IAction>{
class: 'add',
enabled: true,
id: 'addKeybinding',
tooltip: keybinding ? localize('addKeybindingLabelWithKey', "Add Keybinding {0}", `(${keybinding.getLabel()})`) : localize('addKeybindingLabel', "Add Keybinding"),
run: () => this.keybindingsEditor.defineKeybinding(keybindingItemEntry)
};
}
}
class CommandColumn extends Column {
private commandColumn: HTMLElement;
create(parent: HTMLElement): HTMLElement {
this.commandColumn = DOM.append(parent, $('.column.command', { id: 'command_' + ++Column.COUNTER }));
return this.commandColumn;
}
render(keybindingItemEntry: IKeybindingItemEntry): void {
DOM.clearNode(this.commandColumn);
const keybindingItem = keybindingItemEntry.keybindingItem;
const commandIdMatched = !!(keybindingItem.commandLabel && keybindingItemEntry.commandIdMatches);
const commandDefaultLabelMatched = !!keybindingItemEntry.commandDefaultLabelMatches;
DOM.toggleClass(this.commandColumn, 'vertical-align-column', commandIdMatched || commandDefaultLabelMatched);
this.commandColumn.setAttribute('aria-label', this.getAriaLabel(keybindingItemEntry));
if (keybindingItem.commandLabel) {
const commandLabel = new HighlightedLabel(this.commandColumn);
commandLabel.set(keybindingItem.commandLabel, keybindingItemEntry.commandLabelMatches);
commandLabel.element.title = keybindingItem.command;
}
if (keybindingItemEntry.commandDefaultLabelMatches) {
new HighlightedLabel(DOM.append(this.commandColumn, $('.command-default-label'))).set(keybindingItem.commandDefaultLabel, keybindingItemEntry.commandDefaultLabelMatches);
}
if (keybindingItemEntry.commandIdMatches || !keybindingItem.commandLabel) {
new HighlightedLabel(DOM.append(this.commandColumn, $('.code'))).set(keybindingItem.command, keybindingItemEntry.commandIdMatches);
}
}
private getAriaLabel(keybindingItemEntry: IKeybindingItemEntry): string {
return localize('commandAriaLabel', "Command is {0}.", keybindingItemEntry.keybindingItem.commandLabel ? keybindingItemEntry.keybindingItem.commandLabel : keybindingItemEntry.keybindingItem.command);
}
}
class KeybindingColumn extends Column {
private keybindingColumn: HTMLElement;
create(parent: HTMLElement): HTMLElement {
this.keybindingColumn = DOM.append(parent, $('.column.keybinding', { id: 'keybinding_' + ++Column.COUNTER }));
return this.keybindingColumn;
}
render(keybindingItemEntry: IKeybindingItemEntry): void {
DOM.clearNode(this.keybindingColumn);
this.keybindingColumn.setAttribute('aria-label', this.getAriaLabel(keybindingItemEntry));
if (keybindingItemEntry.keybindingItem.keybinding) {
new KeybindingLabel(this.keybindingColumn, OS).set(keybindingItemEntry.keybindingItem.keybinding, keybindingItemEntry.keybindingMatches);
}
}
private getAriaLabel(keybindingItemEntry: IKeybindingItemEntry): string {
return keybindingItemEntry.keybindingItem.keybinding ? localize('keybindingAriaLabel', "Keybinding is {0}.", keybindingItemEntry.keybindingItem.keybinding.getAriaLabel()) : localize('noKeybinding', "No Keybinding assigned.");
}
}
class SourceColumn extends Column {
private sourceColumn: HTMLElement;
create(parent: HTMLElement): HTMLElement {
this.sourceColumn = DOM.append(parent, $('.column.source', { id: 'source_' + ++Column.COUNTER }));
return this.sourceColumn;
}
render(keybindingItemEntry: IKeybindingItemEntry): void {
DOM.clearNode(this.sourceColumn);
this.sourceColumn.setAttribute('aria-label', this.getAriaLabel(keybindingItemEntry));
new HighlightedLabel(this.sourceColumn).set(keybindingItemEntry.keybindingItem.source, keybindingItemEntry.sourceMatches);
}
private getAriaLabel(keybindingItemEntry: IKeybindingItemEntry): string {
return localize('sourceAriaLabel', "Source is {0}.", keybindingItemEntry.keybindingItem.source);
}
}
class WhenColumn extends Column {
private whenColumn: HTMLElement;
create(parent: HTMLElement): HTMLElement {
const column = DOM.append(parent, $('.column.when'));
this.whenColumn = DOM.append(column, $('div', { id: 'when_' + ++Column.COUNTER }));
return this.whenColumn;
}
render(keybindingItemEntry: IKeybindingItemEntry): void {
DOM.clearNode(this.whenColumn);
this.whenColumn.setAttribute('aria-label', this.getAriaLabel(keybindingItemEntry));
DOM.toggleClass(this.whenColumn, 'code', !!keybindingItemEntry.keybindingItem.when);
DOM.toggleClass(this.whenColumn, 'empty', !keybindingItemEntry.keybindingItem.when);
if (keybindingItemEntry.keybindingItem.when) {
new HighlightedLabel(this.whenColumn).set(keybindingItemEntry.keybindingItem.when, keybindingItemEntry.whenMatches);
this.whenColumn.title = keybindingItemEntry.keybindingItem.when;
} else {
this.whenColumn.textContent = '—';
}
}
private getAriaLabel(keybindingItemEntry: IKeybindingItemEntry): string {
return keybindingItemEntry.keybindingItem.when ? localize('whenAriaLabel', "When is {0}.", keybindingItemEntry.keybindingItem.when) : localize('noWhen', "No when context.");
}
}
registerThemingParticipant((theme: ITheme, collector: ICssStyleCollector) => {
const listHighlightForegroundColor = theme.getColor(listHighlightForeground);
if (listHighlightForegroundColor) {
collector.addRule(`.keybindings-editor > .keybindings-body > .keybindings-list-container .monaco-list-row > .column .highlight { color: ${listHighlightForegroundColor}; }`);
}
});

View File

@@ -0,0 +1,394 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
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 { ServicesAccessor, registerEditorCommand, EditorCommand } from 'vs/editor/common/editorCommonExtensions';
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
import { editorContribution } from 'vs/editor/browser/editorBrowserExtensions';
import { SnippetController2 } from 'vs/editor/contrib/snippet/browser/snippetController2';
import { SmartSnippetInserter } from 'vs/workbench/parts/preferences/common/smartSnippetInserter';
import { DefineKeybindingOverlayWidget } from 'vs/workbench/parts/preferences/browser/keybindingWidgets';
import { FloatingClickWidget } from 'vs/workbench/parts/preferences/browser/preferencesWidgets';
import { parseTree, Node } from 'vs/base/common/json';
import { KeybindingIO } from 'vs/workbench/services/keybinding/common/keybindingIO';
import { ScanCodeBinding } from 'vs/workbench/services/keybinding/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';
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$/;
@editorContribution
export class DefineKeybindingController extends Disposable implements editorCommon.IEditorContribution {
private static ID = 'editor.contrib.defineKeybinding';
public static get(editor: editorCommon.ICommonCodeEditor): DefineKeybindingController {
return editor.getContribution<DefineKeybindingController>(DefineKeybindingController.ID);
}
private _keybindingWidgetRenderer: KeybindingWidgetRenderer;
private _keybindingDecorationRenderer: KeybindingEditorDecorationsRenderer;
constructor(
private _editor: ICodeEditor,
@IInstantiationService private _instantiationService: IInstantiationService
) {
super();
this._keybindingWidgetRenderer = null;
this._keybindingDecorationRenderer = null;
this._register(this._editor.onDidChangeModel(e => this._update()));
this._update();
}
public getId(): string {
return DefineKeybindingController.ID;
}
public get keybindingWidgetRenderer(): KeybindingWidgetRenderer {
return this._keybindingWidgetRenderer;
}
public 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 = null;
}
}
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 = null;
}
}
}
export class KeybindingWidgetRenderer extends Disposable {
private _launchWidget: FloatingClickWidget;
private _defineWidget: DefineKeybindingOverlayWidget;
constructor(
private _editor: ICodeEditor,
@IInstantiationService private _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();
}
public showDefineKeybindingWidget(): void {
this._defineWidget.start().then(keybinding => this._onAccepted(keybinding));
}
private _onAccepted(keybinding: string): void {
this._editor.focus();
if (keybinding) {
let regexp = new RegExp(/\\/g);
let 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');
let 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: ICodeEditor,
@IKeybindingService private _keybindingService: IKeybindingService,
) {
super();
this._updateDecorations = this._register(new RunOnceScheduler(() => this._updateDecorationsNow(), 500));
let 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();
let newDecorations: editorCommon.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: editorCommon.IModel, entry: Node): editorCommon.IModelDeltaDecoration {
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;
if (resolvedKeybinding instanceof WindowsNativeResolvedKeybinding) {
usLabel = resolvedKeybinding.getUSLabel();
}
if (!resolvedKeybinding.isWYSIWYG()) {
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 (!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 [parsedA1, parsedA2] = KeybindingIO._readUserBinding(a);
const [parsedB1, parsedB2] = KeybindingIO._readUserBinding(b);
return (
this._userBindingEquals(parsedA1, parsedB1)
&& this._userBindingEquals(parsedA2, parsedB2)
);
}
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, usLabel: string, model: editorCommon.IModel, keyNode: Node): editorCommon.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: editorCommon.TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges,
className: className,
beforeContentClassName: beforeContentClassName,
hoverMessage: msg,
overviewRuler: {
color: overviewRulerColor,
darkColor: overviewRulerColor,
position: editorCommon.OverviewRulerLane.Right
}
}
};
}
}
class DefineKeybindingCommand extends EditorCommand {
static ID = 'editor.action.defineKeybinding';
constructor() {
super({
id: DefineKeybindingCommand.ID,
precondition: ContextKeyExpr.and(EditorContextKeys.writable, EditorContextKeys.languageId.isEqualTo('json')),
kbOpts: {
kbExpr: EditorContextKeys.textFocus,
primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.KEY_K)
}
});
}
public runEditorCommand(accessor: ServicesAccessor, editor: editorCommon.ICommonCodeEditor): void {
if (!isInterestingEditorModel(editor) || editor.getConfiguration().readOnly) {
return;
}
let controller = DefineKeybindingController.get(editor);
if (controller && controller.keybindingWidgetRenderer) {
controller.keybindingWidgetRenderer.showDefineKeybindingWidget();
}
}
}
function isInterestingEditorModel(editor: editorCommon.ICommonCodeEditor): boolean {
let model = editor.getModel();
if (!model) {
return false;
}
let url = model.uri.toString();
return INTERESTING_FILE.test(url);
}
registerEditorCommand(new DefineKeybindingCommand());

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="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"><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,66 @@
/*---------------------------------------------------------------------------------------------
* 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 {
margin-top:10px;
width: 400px;
display: block;
text-align: center;
}
.defineKeybindingWidget .input {
text-align: center;
}
.defineKeybindingWidget .output {
display: flex;
justify-content: center;
}
.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,190 @@
/*---------------------------------------------------------------------------------------------
* 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 > .sort-by-precedence {
position: absolute;
top: 0;
right: 10px;
margin-top: 5px;
background: url('sort_precedence.svg') center center no-repeat;
}
.hc-black .keybindings-editor > .keybindings-header > .search-container > .sort-by-precedence,
.vs-dark .keybindings-editor > .keybindings-header > .search-container > .sort-by-precedence {
background: url('sort_precedence_inverse.svg') center center no-repeat;
}
.keybindings-editor > .keybindings-header .search-container > .settings-search-input {
vertical-align: middle;
}
.keybindings-editor > .keybindings-header .search-container > .settings-search-input > .monaco-inputbox {
height: 30px;
}
.keybindings-editor > .keybindings-header .search-container > .settings-search-input > .monaco-inputbox .input {
font-size: 14px;
padding-left:10px;
}
.keybindings-editor > .keybindings-header .open-keybindings-container {
margin-top: 10px;
opacity: 0.7;
display: flex;
}
.keybindings-editor > .keybindings-header .open-keybindings-container > .file-name {
text-decoration: underline;
cursor: pointer;
margin-left: 4px;
}
/** List based styling **/
.keybindings-editor > .keybindings-body .keybindings-list-container {
width: 100%;
border-spacing: 0;
border-collapse: separate;
}
.keybindings-editor > .keybindings-body > .keybindings-list-container .monaco-list-row {
cursor: default;
display: flex;
}
.keybindings-editor > .keybindings-body > .keybindings-list-container .monaco-list-row.even:not(.focused):not(.selected):not(:hover),
.keybindings-editor > .keybindings-body > .keybindings-list-container .monaco-list:not(:focus) .monaco-list-row.focused.even:not(.selected):not(:hover),
.keybindings-editor > .keybindings-body > .keybindings-list-container .monaco-list:not(.focused) .monaco-list-row.focused.even:not(.selected):not(:hover) {
background-color: rgba(130, 130, 130, 0.04);
}
.keybindings-editor > .keybindings-body .keybindings-list-container .monaco-list-row > .header {
text-align: left;
font-weight: bold;
}
.keybindings-editor > .keybindings-body > .keybindings-list-container .monaco-list-row .header,
.keybindings-editor > .keybindings-body > .keybindings-list-container .monaco-list-row .column {
align-items: center;
display: flex;
overflow: hidden;
margin-right: 6px;
}
.keybindings-editor > .keybindings-body > .keybindings-list-container .monaco-list-row .actions {
width: 24px;
padding-right: 2px;
}
.keybindings-editor > .keybindings-body > .keybindings-list-container .monaco-list-row .command {
flex: 0.75;
}
.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 {
flex: 0.5;
}
.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 .source {
flex: 0.25;
}
.keybindings-editor > .keybindings-body > .keybindings-list-container .monaco-list-row .when {
flex: 1;
}
.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 .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: Monaco, Menlo, Consolas, "Droid Sans Mono", "Inconsolata", "Courier New", monospace, "Droid Sans Fallback";
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-row > .column .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,241 @@
/*---------------------------------------------------------------------------------------------
* 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 {
display: flex;
flex-wrap: wrap;
padding-left: 27px;
padding-right: 32px;
padding-bottom: 11px;
padding-top: 11px;
}
.preferences-editor > .preferences-header.vertical-layout {
flex-direction: column;
align-items: flex-end;
padding-bottom: 5px;
}
.preferences-editor > .preferences-editors-container.side-by-side-preferences-editor {
position: relative;
}
.settings-targets-widget {
flex-wrap: wrap;
margin: 4px 0 4px 18px;
display: flex;
border-radius: 4px;
padding: 0 8px;
cursor: pointer;
}
.settings-targets-widget > .settings-target {
font-size: 11px;
padding: 2px 4px 0 0;
white-space: nowrap;
overflow: hidden;
flex: 1;
display: flex;
}
.settings-targets-widget > .settings-target > .settings-target-label {
text-transform: uppercase;
}
.settings-targets-widget > .settings-target > .settings-target-details {
margin-left: 0.5em;
font-size: 10px;
opacity: 0.7;
}
.settings-targets-widget > .settings-target > .settings-target-details.empty {
margin-left: 0;
}
.settings-targets-widget > .settings-target-dropdown-icon {
padding-left: 0.5em;
padding-top: 4px;
font-size: 12px;
}
.preferences-header > .settings-header-widget {
flex: 1;
display: flex;
position: relative;
align-self: stretch;
}
.settings-header-widget > .settings-header-container {
padding-top: 8px;
padding-left: 27px;
padding-right: 32px;
}
.settings-header-widget > .settings-count-widget {
margin: 6px 0px;
padding: 0px 8px;
position: absolute;
right: 10px;
border-radius: 2px;
}
.settings-header-widget > .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 .settings-header-widget .title-container {
display: flex;
}
.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;
-webkit-user-select: none;
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 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><style>.st0{opacity:0}.st0,.st1{fill:#f6f6f6}.st2{fill:#424242}.st3{fill:#f0eff1}</style><g id="outline"><path class="st0" d="M0 0h16v16H0z"/><path class="st1" d="M5 1v5H0v9h11v-2.586l1 1 4-4V1H5zm5 3v2L8 4.586V4h2zm-2 8H3V9h5v3z"/></g><g id="icon_x5F_bg"><path class="st2" d="M3 9h2v2H3zM6 10h2v2H6zM14 3v3h.586L15 5.586V2H6v4h1V3z"/><path class="st2" d="M9 10.414V13H2V8h6V7H1v7h9V11.414zM9.414 6H10V4H8v.586z"/><path class="st2" d="M13 5h-2v4L9 7v2l3 3 3-3V7l-2 2z"/></g><g id="icon_x5F_fg"><path class="st3" d="M8 9.414V8H2v5h7v-2.586l-1-1zM5 11H3V9h2v2zm3 1H6v-2h2v2zM8 4.586V4h6V3H7v3h1z"/></g></svg>

After

Width:  |  Height:  |  Size: 666 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><style>.st0{opacity:0}.st0,.st1{fill:#2d2d30}.st2{fill:#c5c5c5}.st3{fill:#2b282e}</style><g id="outline"><path class="st0" d="M0 0h16v16H0z"/><path class="st1" d="M5 1v5H0v9h11v-2.586l1 1 4-4V1H5zm5 3v2L8 4.586V4h2zm-2 8H3V9h5v3z"/></g><g id="icon_x5F_bg"><path class="st2" d="M3 9h2v2H3zM6 10h2v2H6zM14 3v3h.586L15 5.586V2H6v4h1V3z"/><path class="st2" d="M9 10.414V13H2V8h6V7H1v7h9V11.414zM9.414 6H10V4H8v.586z"/><path class="st2" d="M13 5h-2v4L9 7v2l3 3 3-3V7l-2 2z"/></g><g id="icon_x5F_fg"><path class="st3" d="M8 9.414V8H2v5h7v-2.586l-1-1zM5 11H3V9h2v2zm3 1H6v-2h2v2zM8 4.586V4h6V3H7v3h1z"/></g></svg>

After

Width:  |  Height:  |  Size: 666 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,249 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import * as nls from 'vs/nls';
import URI from 'vs/base/common/uri';
import { Registry } from 'vs/platform/registry/common/platform';
import { KeybindingsRegistry } from 'vs/platform/keybinding/common/keybindingsRegistry';
import { IWorkbenchActionRegistry, Extensions } from 'vs/workbench/common/actionRegistry';
import { EditorInput, IEditorRegistry, Extensions as EditorExtensions, IEditorInputFactory } from 'vs/workbench/common/editor';
import { EditorDescriptor } from 'vs/workbench/browser/parts/editor/baseEditor';
import { SyncActionDescriptor } from 'vs/platform/actions/common/actions';
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
import { KeyMod, KeyChord, KeyCode } from 'vs/base/common/keyCodes';
import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors';
import { DefaultPreferencesEditorInput, PreferencesEditor, PreferencesEditorInput } from 'vs/workbench/parts/preferences/browser/preferencesEditor';
import { KeybindingsEditor, KeybindingsEditorInput } from 'vs/workbench/parts/preferences/browser/keybindingsEditor';
import { OpenGlobalSettingsAction, OpenGlobalKeybindingsAction, OpenGlobalKeybindingsFileAction, OpenWorkspaceSettingsAction, OpenFolderSettingsAction, ConfigureLanguageBasedSettingsAction } from 'vs/workbench/parts/preferences/browser/preferencesActions';
import {
IPreferencesService, IKeybindingsEditor, CONTEXT_KEYBINDING_FOCUS, CONTEXT_KEYBINDINGS_EDITOR, CONTEXT_KEYBINDINGS_SEARCH_FOCUS, KEYBINDINGS_EDITOR_COMMAND_DEFINE, KEYBINDINGS_EDITOR_COMMAND_REMOVE, KEYBINDINGS_EDITOR_COMMAND_SEARCH,
KEYBINDINGS_EDITOR_COMMAND_COPY, KEYBINDINGS_EDITOR_COMMAND_RESET, KEYBINDINGS_EDITOR_COMMAND_SHOW_CONFLICTS, KEYBINDINGS_EDITOR_COMMAND_FOCUS_KEYBINDINGS
} from 'vs/workbench/parts/preferences/common/preferences';
import { PreferencesService } from 'vs/workbench/parts/preferences/browser/preferencesService';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } from 'vs/workbench/common/contributions';
import { PreferencesContentProvider } from 'vs/workbench/parts/preferences/common/preferencesContentProvider';
import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService';
registerSingleton(IPreferencesService, PreferencesService);
Registry.as<IEditorRegistry>(EditorExtensions.Editors).registerEditor(
new EditorDescriptor(
PreferencesEditor.ID,
nls.localize('defaultPreferencesEditor', "Default Preferences Editor"),
'vs/workbench/parts/preferences/browser/preferencesEditor',
'PreferencesEditor'
),
[
new SyncDescriptor(PreferencesEditorInput)
]
);
Registry.as<IEditorRegistry>(EditorExtensions.Editors).registerEditor(
new EditorDescriptor(
KeybindingsEditor.ID,
nls.localize('keybindingsEditor', "Keybindings Editor"),
'vs/workbench/parts/preferences/browser/keybindingsEditor',
'KeybindingsEditor'
),
[
new SyncDescriptor(KeybindingsEditorInput)
]
);
interface ISerializedPreferencesEditorInput {
name: string;
description: string;
detailsSerialized: string;
masterSerialized: string;
detailsTypeId: string;
masterTypeId: string;
}
// Register Preferences Editor Input Factory
class PreferencesEditorInputFactory implements IEditorInputFactory {
public serialize(editorInput: EditorInput): string {
const input = <PreferencesEditorInput>editorInput;
if (input.details && input.master) {
const registry = Registry.as<IEditorRegistry>(EditorExtensions.Editors);
const detailsInputFactory = registry.getEditorInputFactory(input.details.getTypeId());
const masterInputFactory = registry.getEditorInputFactory(input.master.getTypeId());
if (detailsInputFactory && masterInputFactory) {
const detailsSerialized = detailsInputFactory.serialize(input.details);
const masterSerialized = masterInputFactory.serialize(input.master);
if (detailsSerialized && masterSerialized) {
return JSON.stringify(<ISerializedPreferencesEditorInput>{
name: input.getName(),
description: input.getDescription(),
detailsSerialized,
masterSerialized,
detailsTypeId: input.details.getTypeId(),
masterTypeId: input.master.getTypeId()
});
}
}
}
return null;
}
public deserialize(instantiationService: IInstantiationService, serializedEditorInput: string): EditorInput {
const deserialized: ISerializedPreferencesEditorInput = JSON.parse(serializedEditorInput);
const registry = Registry.as<IEditorRegistry>(EditorExtensions.Editors);
const detailsInputFactory = registry.getEditorInputFactory(deserialized.detailsTypeId);
const masterInputFactory = registry.getEditorInputFactory(deserialized.masterTypeId);
if (detailsInputFactory && masterInputFactory) {
const detailsInput = detailsInputFactory.deserialize(instantiationService, deserialized.detailsSerialized);
const masterInput = masterInputFactory.deserialize(instantiationService, deserialized.masterSerialized);
if (detailsInput && masterInput) {
return new PreferencesEditorInput(deserialized.name, deserialized.description, detailsInput, masterInput);
}
}
return null;
}
}
class KeybindingsEditorInputFactory implements IEditorInputFactory {
public serialize(editorInput: EditorInput): string {
const input = <KeybindingsEditorInput>editorInput;
return JSON.stringify({
name: input.getName(),
typeId: input.getTypeId()
});
}
public deserialize(instantiationService: IInstantiationService, serializedEditorInput: string): EditorInput {
return instantiationService.createInstance(KeybindingsEditorInput);
}
}
interface ISerializedDefaultPreferencesEditorInput {
resource: string;
}
// Register Default Preferences Editor Input Factory
class DefaultPreferencesEditorInputFactory implements IEditorInputFactory {
public serialize(editorInput: EditorInput): string {
const input = <DefaultPreferencesEditorInput>editorInput;
const serialized: ISerializedDefaultPreferencesEditorInput = { resource: input.getResource().toString() };
return JSON.stringify(serialized);
}
public deserialize(instantiationService: IInstantiationService, serializedEditorInput: string): EditorInput {
const deserialized: ISerializedDefaultPreferencesEditorInput = JSON.parse(serializedEditorInput);
return instantiationService.createInstance(DefaultPreferencesEditorInput, URI.parse(deserialized.resource));
}
}
Registry.as<IEditorRegistry>(EditorExtensions.Editors).registerEditorInputFactory(PreferencesEditorInput.ID, PreferencesEditorInputFactory);
Registry.as<IEditorRegistry>(EditorExtensions.Editors).registerEditorInputFactory(DefaultPreferencesEditorInput.ID, DefaultPreferencesEditorInputFactory);
Registry.as<IEditorRegistry>(EditorExtensions.Editors).registerEditorInputFactory(KeybindingsEditorInput.ID, KeybindingsEditorInputFactory);
// Contribute Global Actions
const category = nls.localize('preferences', "Preferences");
const registry = Registry.as<IWorkbenchActionRegistry>(Extensions.WorkbenchActions);
registry.registerWorkbenchAction(new SyncActionDescriptor(OpenGlobalSettingsAction, OpenGlobalSettingsAction.ID, OpenGlobalSettingsAction.LABEL, { primary: KeyMod.CtrlCmd | KeyCode.US_COMMA }), 'Preferences: Open User Settings', category);
registry.registerWorkbenchAction(new SyncActionDescriptor(OpenWorkspaceSettingsAction, OpenWorkspaceSettingsAction.ID, OpenWorkspaceSettingsAction.LABEL), 'Preferences: Open Workspace Settings', category);
registry.registerWorkbenchAction(new SyncActionDescriptor(OpenFolderSettingsAction, OpenFolderSettingsAction.ID, OpenFolderSettingsAction.LABEL), 'Preferences: Open Folder Settings', category);
registry.registerWorkbenchAction(new SyncActionDescriptor(OpenGlobalKeybindingsAction, OpenGlobalKeybindingsAction.ID, OpenGlobalKeybindingsAction.LABEL, { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.KEY_S) }), 'Preferences: Open Keyboard Shortcuts', category);
registry.registerWorkbenchAction(new SyncActionDescriptor(OpenGlobalKeybindingsFileAction, OpenGlobalKeybindingsFileAction.ID, OpenGlobalKeybindingsFileAction.LABEL, { primary: null }), 'Preferences: Open Keyboard Shortcuts File', category);
registry.registerWorkbenchAction(new SyncActionDescriptor(ConfigureLanguageBasedSettingsAction, ConfigureLanguageBasedSettingsAction.ID, ConfigureLanguageBasedSettingsAction.LABEL), 'Preferences: Configure Language Specific Settings...', category);
KeybindingsRegistry.registerCommandAndKeybindingRule({
id: KEYBINDINGS_EDITOR_COMMAND_DEFINE,
weight: KeybindingsRegistry.WEIGHT.workbenchContrib(),
when: ContextKeyExpr.and(CONTEXT_KEYBINDINGS_EDITOR, CONTEXT_KEYBINDING_FOCUS),
primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.KEY_K),
handler: (accessor, args: any) => {
const editor = accessor.get(IWorkbenchEditorService).getActiveEditor() as IKeybindingsEditor;
editor.defineKeybinding(editor.activeKeybindingEntry);
}
});
KeybindingsRegistry.registerCommandAndKeybindingRule({
id: KEYBINDINGS_EDITOR_COMMAND_REMOVE,
weight: KeybindingsRegistry.WEIGHT.workbenchContrib(),
when: ContextKeyExpr.and(CONTEXT_KEYBINDINGS_EDITOR, CONTEXT_KEYBINDING_FOCUS),
primary: KeyCode.Delete,
mac: {
primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.Backspace)
},
handler: (accessor, args: any) => {
const editor = accessor.get(IWorkbenchEditorService).getActiveEditor() as IKeybindingsEditor;
editor.removeKeybinding(editor.activeKeybindingEntry);
}
});
KeybindingsRegistry.registerCommandAndKeybindingRule({
id: KEYBINDINGS_EDITOR_COMMAND_RESET,
weight: KeybindingsRegistry.WEIGHT.workbenchContrib(),
when: ContextKeyExpr.and(CONTEXT_KEYBINDINGS_EDITOR, CONTEXT_KEYBINDING_FOCUS),
primary: null,
handler: (accessor, args: any) => {
const editor = accessor.get(IWorkbenchEditorService).getActiveEditor() as IKeybindingsEditor;
editor.resetKeybinding(editor.activeKeybindingEntry);
}
});
KeybindingsRegistry.registerCommandAndKeybindingRule({
id: KEYBINDINGS_EDITOR_COMMAND_SEARCH,
weight: KeybindingsRegistry.WEIGHT.workbenchContrib(),
when: ContextKeyExpr.and(CONTEXT_KEYBINDINGS_EDITOR, CONTEXT_KEYBINDING_FOCUS),
primary: KeyMod.CtrlCmd | KeyCode.KEY_F,
handler: (accessor, args: any) => (accessor.get(IWorkbenchEditorService).getActiveEditor() as IKeybindingsEditor).search('')
});
KeybindingsRegistry.registerCommandAndKeybindingRule({
id: KEYBINDINGS_EDITOR_COMMAND_SHOW_CONFLICTS,
weight: KeybindingsRegistry.WEIGHT.workbenchContrib(),
when: ContextKeyExpr.and(CONTEXT_KEYBINDINGS_EDITOR, CONTEXT_KEYBINDING_FOCUS),
primary: null,
handler: (accessor, args: any) => {
const editor = accessor.get(IWorkbenchEditorService).getActiveEditor() as IKeybindingsEditor;
editor.showConflicts(editor.activeKeybindingEntry);
}
});
KeybindingsRegistry.registerCommandAndKeybindingRule({
id: KEYBINDINGS_EDITOR_COMMAND_COPY,
weight: KeybindingsRegistry.WEIGHT.workbenchContrib(),
when: ContextKeyExpr.and(CONTEXT_KEYBINDINGS_EDITOR, CONTEXT_KEYBINDING_FOCUS),
primary: KeyMod.CtrlCmd | KeyCode.KEY_C,
handler: (accessor, args: any) => {
const editor = accessor.get(IWorkbenchEditorService).getActiveEditor() as IKeybindingsEditor;
editor.copyKeybinding(editor.activeKeybindingEntry);
}
});
KeybindingsRegistry.registerCommandAndKeybindingRule({
id: KEYBINDINGS_EDITOR_COMMAND_FOCUS_KEYBINDINGS,
weight: KeybindingsRegistry.WEIGHT.workbenchContrib(),
when: ContextKeyExpr.and(CONTEXT_KEYBINDINGS_EDITOR, CONTEXT_KEYBINDINGS_SEARCH_FOCUS),
primary: KeyCode.DownArrow,
handler: (accessor, args: any) => {
const editor = accessor.get(IWorkbenchEditorService).getActiveEditor() as IKeybindingsEditor;
editor.focusKeybindings();
}
});
Registry.as<IWorkbenchContributionsRegistry>(WorkbenchExtensions.Workbench).registerWorkbenchContribution(PreferencesContentProvider);

View File

@@ -0,0 +1,173 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import { TPromise } from 'vs/base/common/winjs.base';
import * as nls from 'vs/nls';
import URI from 'vs/base/common/uri';
import { Action } from 'vs/base/common/actions';
import { IModeService } from 'vs/editor/common/services/modeService';
import { IQuickOpenService, IPickOpenEntry, IFilePickOpenEntry } from 'vs/platform/quickOpen/common/quickOpen';
import { IPreferencesService, getSettingsTargetName } from 'vs/workbench/parts/preferences/common/preferences';
import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
import { ConfigurationTarget } from 'vs/workbench/services/configuration/common/configurationEditing';
export class OpenGlobalSettingsAction extends Action {
public static ID = 'workbench.action.openGlobalSettings';
public static LABEL = nls.localize('openGlobalSettings', "Open User Settings");
constructor(
id: string,
label: string,
@IPreferencesService private preferencesService: IPreferencesService
) {
super(id, label);
}
public run(event?: any): TPromise<any> {
return this.preferencesService.openGlobalSettings();
}
}
export class OpenGlobalKeybindingsAction extends Action {
public static ID = 'workbench.action.openGlobalKeybindings';
public static LABEL = nls.localize('openGlobalKeybindings', "Open Keyboard Shortcuts");
constructor(
id: string,
label: string,
@IPreferencesService private preferencesService: IPreferencesService
) {
super(id, label);
}
public run(event?: any): TPromise<any> {
return this.preferencesService.openGlobalKeybindingSettings(false);
}
}
export class OpenGlobalKeybindingsFileAction extends Action {
public static ID = 'workbench.action.openGlobalKeybindingsFile';
public static LABEL = nls.localize('openGlobalKeybindingsFile', "Open Keyboard Shortcuts File");
constructor(
id: string,
label: string,
@IPreferencesService private preferencesService: IPreferencesService
) {
super(id, label);
}
public run(event?: any): TPromise<any> {
return this.preferencesService.openGlobalKeybindingSettings(true);
}
}
export class OpenWorkspaceSettingsAction extends Action {
public static ID = 'workbench.action.openWorkspaceSettings';
public static LABEL = nls.localize('openWorkspaceSettings', "Open Workspace Settings");
constructor(
id: string,
label: string,
@IPreferencesService private preferencesService: IPreferencesService,
@IWorkspaceContextService private workspaceContextService: IWorkspaceContextService
) {
super(id, label);
this.enabled = this.workspaceContextService.hasWorkspace();
}
public run(event?: any): TPromise<any> {
return this.preferencesService.openWorkspaceSettings();
}
}
export class OpenFolderSettingsAction extends Action {
public static ID = 'workbench.action.openFolderSettings';
public static LABEL = nls.localize('openFolderSettings', "Open Folder Settings");
constructor(
id: string,
label: string,
@IPreferencesService private preferencesService: IPreferencesService,
@IWorkspaceContextService private workspaceContextService: IWorkspaceContextService,
@IQuickOpenService private quickOpenService: IQuickOpenService
) {
super(id, label);
this.enabled = this.workspaceContextService.hasMultiFolderWorkspace();
}
public run(): TPromise<any> {
const picks: IPickOpenEntry[] = this.workspaceContextService.getWorkspace().roots.map((root, index) => {
return <IPickOpenEntry>{
label: getSettingsTargetName(ConfigurationTarget.FOLDER, root, this.workspaceContextService),
id: `${index}`
};
});
return this.quickOpenService.pick(picks, { placeHolder: nls.localize('pickFolder', "Select Folder") })
.then(pick => {
if (pick) {
return this.preferencesService.openFolderSettings(this.workspaceContextService.getWorkspace().roots[parseInt(pick.id)]);
}
return undefined;
});
}
}
export class ConfigureLanguageBasedSettingsAction extends Action {
public static ID = 'workbench.action.configureLanguageBasedSettings';
public static LABEL = nls.localize('configureLanguageBasedSettings', "Configure Language Specific Settings...");
constructor(
id: string,
label: string,
@IModeService private modeService: IModeService,
@IQuickOpenService private quickOpenService: IQuickOpenService,
@IPreferencesService private preferencesService: IPreferencesService
) {
super(id, label);
}
public run(): TPromise<any> {
const languages = this.modeService.getRegisteredLanguageNames();
const picks: IPickOpenEntry[] = languages.sort().map((lang, index) => {
let 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;
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 <IFilePickOpenEntry>{
label: lang,
resource: fakeResource,
description
};
});
return this.quickOpenService.pick(picks, { placeHolder: nls.localize('pickLanguage', "Select Language") })
.then(pick => {
if (pick) {
return this.modeService.getOrCreateModeByLanguageName(pick.label)
.then(mode => this.preferencesService.configureSettingsForLanguage(mode.getLanguageIdentifier().language));
}
return undefined;
});
}
}

View File

@@ -0,0 +1,948 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { TPromise } from 'vs/base/common/winjs.base';
import * as nls from 'vs/nls';
import URI from 'vs/base/common/uri';
import * as DOM from 'vs/base/browser/dom';
import { Delayer } from 'vs/base/common/async';
import { Dimension, Builder } from 'vs/base/browser/builder';
import { ArrayNavigator, INavigator } from 'vs/base/common/iterator';
import { Disposable, IDisposable, dispose } from 'vs/base/common/lifecycle';
import { KeyMod, KeyCode } from 'vs/base/common/keyCodes';
import { toResource, SideBySideEditorInput, EditorOptions, EditorInput, IEditorRegistry, Extensions as EditorExtensions } from 'vs/workbench/common/editor';
import { BaseEditor, EditorDescriptor } from 'vs/workbench/browser/parts/editor/baseEditor';
import { ResourceEditorModel } from 'vs/workbench/common/editor/resourceEditorModel';
import { IEditorControl, Position, Verbosity } from 'vs/platform/editor/common/editor';
import { ResourceEditorInput } from 'vs/workbench/common/editor/resourceEditorInput';
import * as editorCommon from 'vs/editor/common/editorCommon';
import { BaseTextEditor } from 'vs/workbench/browser/parts/editor/textEditor';
import { CodeEditor } from 'vs/editor/browser/codeEditor';
import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
import {
IPreferencesService, ISettingsGroup, ISetting, IFilterResult,
CONTEXT_SETTINGS_EDITOR, CONTEXT_SETTINGS_SEARCH_FOCUS, SETTINGS_EDITOR_COMMAND_SEARCH, SETTINGS_EDITOR_COMMAND_FOCUS_FILE, ISettingsEditorModel
} from 'vs/workbench/parts/preferences/common/preferences';
import { SettingsEditorModel, DefaultSettingsEditorModel } from 'vs/workbench/parts/preferences/common/preferencesModels';
import { editorContribution } from 'vs/editor/browser/editorBrowserExtensions';
import { ICodeEditor, IEditorContributionCtor } from 'vs/editor/browser/editorBrowser';
import { SearchWidget, SettingsTargetsWidget } from 'vs/workbench/parts/preferences/browser/preferencesWidgets';
import { ContextKeyExpr, IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey';
import { Command } from 'vs/editor/common/editorCommonExtensions';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { IThemeService } from 'vs/platform/theme/common/themeService';
import { IModelService } from 'vs/editor/common/services/modelService';
import { IModeService } from 'vs/editor/common/services/modeService';
import { IStorageService } from 'vs/platform/storage/common/storage';
import { ITextResourceConfigurationService } from 'vs/editor/common/services/resourceConfiguration';
import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService';
import { ITextModelService } from 'vs/editor/common/services/resolverService';
import { ConfigurationTarget } from 'vs/workbench/services/configuration/common/configurationEditing';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { VSash } from 'vs/base/browser/ui/sash/sash';
import { Widget } from 'vs/base/browser/ui/widget';
import { IPreferencesRenderer, DefaultSettingsRenderer, UserSettingsRenderer, WorkspaceSettingsRenderer, FolderSettingsRenderer } from 'vs/workbench/parts/preferences/browser/preferencesRenderers';
import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles';
import { IEditorGroupService } from 'vs/workbench/services/group/common/groupService';
import { getCodeEditor } from 'vs/editor/common/services/codeEditorService';
// Ignore following contributions
import { FoldingController } from 'vs/editor/contrib/folding/browser/folding';
import { FindController } from 'vs/editor/contrib/find/browser/find';
import { SelectionHighlighter } from 'vs/editor/contrib/find/common/findController';
import { IEditorOptions } from 'vs/editor/common/config/editorOptions';
import { KeybindingsRegistry } from 'vs/platform/keybinding/common/keybindingsRegistry';
import { attachStylerCallback } from 'vs/platform/theme/common/styler';
import { scrollbarShadow } from 'vs/platform/theme/common/colorRegistry';
import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
import Event, { Emitter } from 'vs/base/common/event';
import { Registry } from 'vs/platform/registry/common/platform';
export class PreferencesEditorInput extends SideBySideEditorInput {
public static ID: string = 'workbench.editorinputs.preferencesEditorInput';
getTypeId(): string {
return PreferencesEditorInput.ID;
}
public getTitle(verbosity: Verbosity): string {
return this.master.getTitle(verbosity);
}
}
export class DefaultPreferencesEditorInput extends ResourceEditorInput {
public static ID = 'workbench.editorinputs.defaultpreferences';
constructor(defaultSettingsResource: URI,
@ITextModelService textModelResolverService: ITextModelService
) {
super(nls.localize('settingsEditorName', "Default Settings"), '', defaultSettingsResource, textModelResolverService);
}
getTypeId(): string {
return DefaultPreferencesEditorInput.ID;
}
matches(other: any): boolean {
if (!super.matches(other)) {
return false;
}
if (!(other instanceof DefaultPreferencesEditorInput)) {
return false;
}
return true;
}
}
export class PreferencesEditor extends BaseEditor {
public static ID: string = 'workbench.editor.preferencesEditor';
private defaultSettingsEditorContextKey: IContextKey<boolean>;
private focusSettingsContextKey: IContextKey<boolean>;
private headerContainer: HTMLElement;
private searchWidget: SearchWidget;
private settingsTargetsWidget: SettingsTargetsWidget;
private sideBySidePreferencesWidget: SideBySidePreferencesWidget;
private preferencesRenderers: PreferencesRenderers;
private delayedFilterLogging: Delayer<void>;
private latestEmptyFilters: string[] = [];
private lastFocusedWidget: SearchWidget | SideBySidePreferencesWidget = null;
constructor(
@IPreferencesService private preferencesService: IPreferencesService,
@IEnvironmentService private environmentService: IEnvironmentService,
@ITelemetryService telemetryService: ITelemetryService,
@IWorkbenchEditorService private editorService: IWorkbenchEditorService,
@IContextKeyService private contextKeyService: IContextKeyService,
@IInstantiationService private instantiationService: IInstantiationService,
@IThemeService themeService: IThemeService,
@IWorkspaceContextService private workspaceContextService: IWorkspaceContextService,
) {
super(PreferencesEditor.ID, telemetryService, themeService);
this.defaultSettingsEditorContextKey = CONTEXT_SETTINGS_EDITOR.bindTo(this.contextKeyService);
this.focusSettingsContextKey = CONTEXT_SETTINGS_SEARCH_FOCUS.bindTo(this.contextKeyService);
this.delayedFilterLogging = new Delayer<void>(1000);
}
public createEditor(parent: Builder): void {
const parentElement = parent.getHTMLElement();
DOM.addClass(parentElement, 'preferences-editor');
this.headerContainer = DOM.append(parentElement, DOM.$('.preferences-header'));
this.searchWidget = this._register(this.instantiationService.createInstance(SearchWidget, this.headerContainer, {
ariaLabel: nls.localize('SearchSettingsWidget.AriaLabel', "Search settings"),
placeholder: nls.localize('SearchSettingsWidget.Placeholder', "Search Settings"),
focusKey: this.focusSettingsContextKey
}));
this._register(this.searchWidget.onDidChange(value => this.filterPreferences(value.trim())));
this._register(this.searchWidget.onNavigate(shift => this.preferencesRenderers.focusNextPreference(!shift)));
this._register(this.searchWidget.onFocus(() => this.lastFocusedWidget = this.searchWidget));
this.lastFocusedWidget = this.searchWidget;
this.settingsTargetsWidget = this._register(this.instantiationService.createInstance(SettingsTargetsWidget, this.headerContainer, this.preferencesService.userSettingsResource, ConfigurationTarget.USER));
this._register(this.settingsTargetsWidget.onDidTargetChange(target => this.switchSettings(target)));
const editorsContainer = DOM.append(parentElement, DOM.$('.preferences-editors-container'));
this.sideBySidePreferencesWidget = this._register(this.instantiationService.createInstance(SideBySidePreferencesWidget, editorsContainer));
this._register(this.sideBySidePreferencesWidget.onFocus(() => this.lastFocusedWidget = this.sideBySidePreferencesWidget));
this.preferencesRenderers = this._register(new PreferencesRenderers());
this._register(this.workspaceContextService.onDidChangeWorkspaceRoots(() => this.onWorkspaceRootsChanged()));
}
public setInput(newInput: PreferencesEditorInput, options?: EditorOptions): TPromise<void> {
this.defaultSettingsEditorContextKey.set(true);
const oldInput = <PreferencesEditorInput>this.input;
return super.setInput(newInput, options).then(() => this.updateInput(oldInput, newInput, options));
}
public layout(dimension: Dimension): void {
DOM.toggleClass(this.headerContainer, 'vertical-layout', dimension.width < 700);
this.searchWidget.layout(dimension);
const headerHeight = DOM.getTotalHeight(this.headerContainer);
this.sideBySidePreferencesWidget.layout(new Dimension(dimension.width, dimension.height - headerHeight));
}
public getControl(): IEditorControl {
return this.sideBySidePreferencesWidget.getControl();
}
public focus(): void {
if (this.lastFocusedWidget) {
this.lastFocusedWidget.focus();
}
}
public focusSearch(filter?: string): void {
if (filter) {
this.searchWidget.setValue(filter);
}
this.searchWidget.focus();
}
public focusSettingsFileEditor(): void {
if (this.sideBySidePreferencesWidget) {
this.sideBySidePreferencesWidget.focus();
}
}
public clearInput(): void {
this.defaultSettingsEditorContextKey.set(false);
this.sideBySidePreferencesWidget.clearInput();
super.clearInput();
}
protected setEditorVisible(visible: boolean, position: Position): void {
this.sideBySidePreferencesWidget.setEditorVisible(visible, position);
super.setEditorVisible(visible, position);
}
public changePosition(position: Position): void {
this.sideBySidePreferencesWidget.changePosition(position);
super.changePosition(position);
}
private updateInput(oldInput: PreferencesEditorInput, newInput: PreferencesEditorInput, options?: EditorOptions): TPromise<void> {
const resource = toResource(newInput.master);
this.settingsTargetsWidget.setTarget(this.getSettingsConfigurationTargetUri(resource), this.getSettingsConfigurationTarget(resource));
return this.sideBySidePreferencesWidget.setInput(<DefaultPreferencesEditorInput>newInput.details, <EditorInput>newInput.master, options).then(({ defaultPreferencesRenderer, editablePreferencesRenderer }) => {
this.preferencesRenderers.defaultPreferencesRenderer = defaultPreferencesRenderer;
this.preferencesRenderers.editablePreferencesRenderer = editablePreferencesRenderer;
this.filterPreferences(this.searchWidget.getValue());
});
}
private getSettingsConfigurationTarget(resource: URI): ConfigurationTarget {
if (this.preferencesService.userSettingsResource.fsPath === resource.fsPath) {
return ConfigurationTarget.USER;
}
if (this.preferencesService.workspaceSettingsResource.fsPath === resource.fsPath) {
return ConfigurationTarget.WORKSPACE;
}
if (this.workspaceContextService.getRoot(resource)) {
return ConfigurationTarget.FOLDER;
}
return null;
}
private getSettingsConfigurationTargetUri(resource: URI): URI {
if (this.preferencesService.userSettingsResource.fsPath === resource.fsPath) {
return resource;
}
if (this.preferencesService.workspaceSettingsResource.fsPath === resource.fsPath) {
return resource;
}
return this.workspaceContextService.getRoot(resource);
}
private onWorkspaceRootsChanged(): void {
if (this.input) {
const settingsResource = toResource((<PreferencesEditorInput>this.input).master);
const targetResource = this.getSettingsConfigurationTargetUri(settingsResource);
if (!targetResource) {
this.switchSettings(this.preferencesService.userSettingsResource);
}
}
}
private switchSettings(resource: URI): void {
// Focus the editor if this editor is not active editor
if (this.editorService.getActiveEditor() !== this) {
this.focus();
}
const promise = this.input.isDirty() ? this.input.save() : TPromise.as(true);
promise.done(value => this.preferencesService.switchSettings(this.getSettingsConfigurationTarget(resource), resource));
}
private filterPreferences(filter: string) {
const count = this.preferencesRenderers.filterPreferences(filter);
const message = filter ? this.showSearchResultsMessage(count) : nls.localize('totalSettingsMessage', "Total {0} Settings", count);
this.searchWidget.showMessage(message, count);
if (count === 0) {
this.latestEmptyFilters.push(filter);
}
this.delayedFilterLogging.trigger(() => this.reportFilteringUsed(filter));
}
private showSearchResultsMessage(count: number): string {
return count === 0 ? nls.localize('noSettingsFound', "No Results") :
count === 1 ? nls.localize('oneSettingFound', "1 Setting matched") :
nls.localize('settingsFound', "{0} Settings matched", count);
}
private reportFilteringUsed(filter: string): void {
if (filter) {
let data = {
filter,
emptyFilters: this.getLatestEmptyFiltersForTelemetry()
};
this.latestEmptyFilters = [];
this.telemetryService.publicLog('defaultSettings.filter', data);
}
}
/**
* Put a rough limit on the size of the telemetry data, since otherwise it could be an unbounded large amount
* of data. 8192 is the max size of a property value. This is rough since that probably includes ""s, etc.
*/
private getLatestEmptyFiltersForTelemetry(): string[] {
let cumulativeSize = 0;
return this.latestEmptyFilters.filter(filterText => (cumulativeSize += filterText.length) <= 8192);
}
}
class SettingsNavigator implements INavigator<ISetting> {
private iterator: ArrayNavigator<ISetting>;
constructor(settings: ISetting[]) {
this.iterator = new ArrayNavigator<ISetting>(settings);
}
public next(): ISetting {
return this.iterator.next() || this.iterator.first();
}
public previous(): ISetting {
return this.iterator.previous() || this.iterator.last();
}
public parent(): ISetting {
return this.iterator.parent();
}
public first(): ISetting {
return this.iterator.first();
}
public last(): ISetting {
return this.iterator.last();
}
public current(): ISetting {
return this.iterator.current();
}
}
class PreferencesRenderers extends Disposable {
private _defaultPreferencesRenderer: IPreferencesRenderer<ISetting>;
private _editablePreferencesRenderer: IPreferencesRenderer<ISetting>;
private _settingsNavigator: SettingsNavigator;
private _disposables: IDisposable[] = [];
public get defaultPreferencesRenderer(): IPreferencesRenderer<ISetting> {
return this._defaultPreferencesRenderer;
}
public set defaultPreferencesRenderer(defaultPreferencesRenderer: IPreferencesRenderer<ISetting>) {
if (this._defaultPreferencesRenderer !== defaultPreferencesRenderer) {
this._defaultPreferencesRenderer = defaultPreferencesRenderer;
this._disposables = dispose(this._disposables);
if (this._defaultPreferencesRenderer) {
this._defaultPreferencesRenderer.onUpdatePreference(({ key, value, source }) => this._updatePreference(key, value, source, this._editablePreferencesRenderer), this, this._disposables);
this._defaultPreferencesRenderer.onFocusPreference(preference => this._focusPreference(preference, this._editablePreferencesRenderer), this, this._disposables);
this._defaultPreferencesRenderer.onClearFocusPreference(preference => this._clearFocus(preference, this._editablePreferencesRenderer), this, this._disposables);
}
}
}
public set editablePreferencesRenderer(editableSettingsRenderer: IPreferencesRenderer<ISetting>) {
this._editablePreferencesRenderer = editableSettingsRenderer;
}
public filterPreferences(filter: string): number {
const defaultPreferencesFilterResult = this._filterPreferences(filter, this._defaultPreferencesRenderer);
const editablePreferencesFilterResult = this._filterPreferences(filter, this._editablePreferencesRenderer);
const defaultPreferencesFilteredGroups = defaultPreferencesFilterResult ? defaultPreferencesFilterResult.filteredGroups : this._getAllPreferences(this._defaultPreferencesRenderer);
const editablePreferencesFilteredGroups = editablePreferencesFilterResult ? editablePreferencesFilterResult.filteredGroups : this._getAllPreferences(this._editablePreferencesRenderer);
const consolidatedSettings = this._consolidateSettings(editablePreferencesFilteredGroups, defaultPreferencesFilteredGroups);
this._settingsNavigator = new SettingsNavigator(filter ? consolidatedSettings : []);
return consolidatedSettings.length;
}
public focusNextPreference(forward: boolean = true) {
const setting = forward ? this._settingsNavigator.next() : this._settingsNavigator.previous();
this._focusPreference(setting, this._defaultPreferencesRenderer);
this._focusPreference(setting, this._editablePreferencesRenderer);
}
private _getAllPreferences(preferencesRenderer: IPreferencesRenderer<ISetting>): ISettingsGroup[] {
return preferencesRenderer ? (<ISettingsEditorModel>preferencesRenderer.preferencesModel).settingsGroups : [];
}
private _filterPreferences(filter: string, preferencesRenderer: IPreferencesRenderer<ISetting>): IFilterResult {
let filterResult = null;
if (preferencesRenderer) {
filterResult = filter ? (<ISettingsEditorModel>preferencesRenderer.preferencesModel).filterSettings(filter) : null;
preferencesRenderer.filterPreferences(filterResult);
}
return filterResult;
}
private _focusPreference(preference: ISetting, preferencesRenderer: IPreferencesRenderer<ISetting>): void {
if (preference && preferencesRenderer) {
preferencesRenderer.focusPreference(preference);
}
}
private _clearFocus(preference: ISetting, preferencesRenderer: IPreferencesRenderer<ISetting>): void {
if (preference && preferencesRenderer) {
preferencesRenderer.clearFocus(preference);
}
}
private _updatePreference(key: string, value: any, source: ISetting, preferencesRenderer: IPreferencesRenderer<ISetting>): void {
if (preferencesRenderer) {
preferencesRenderer.updatePreference(key, value, source);
}
}
private _consolidateSettings(editableSettingsGroups: ISettingsGroup[], defaultSettingsGroups: ISettingsGroup[]): ISetting[] {
const editableSettings = this._flatten(editableSettingsGroups);
const defaultSettings = this._flatten(defaultSettingsGroups).filter(secondarySetting => !editableSettings.some(primarySetting => primarySetting.key === secondarySetting.key));
return [...editableSettings, ...defaultSettings];
}
private _flatten(settingsGroups: ISettingsGroup[]): ISetting[] {
const settings: ISetting[] = [];
for (const group of settingsGroups) {
for (const section of group.sections) {
settings.push(...section.settings);
}
}
return settings;
}
public dispose(): void {
dispose(this._disposables);
super.dispose();
}
}
class SideBySidePreferencesWidget extends Widget {
private dimension: Dimension;
private defaultPreferencesEditor: DefaultPreferencesEditor;
private editablePreferencesEditor: BaseEditor;
private defaultPreferencesEditorContainer: HTMLElement;
private editablePreferencesEditorContainer: HTMLElement;
private _onFocus: Emitter<void> = new Emitter<void>();
readonly onFocus: Event<void> = this._onFocus.event;
private lastFocusedEditor: BaseEditor;
private sash: VSash;
constructor(parent: HTMLElement, @IInstantiationService private instantiationService: IInstantiationService, @IThemeService private themeService: IThemeService) {
super();
this.create(parent);
}
private create(parentElement: HTMLElement): void {
DOM.addClass(parentElement, 'side-by-side-preferences-editor');
this.createSash(parentElement);
this.defaultPreferencesEditorContainer = DOM.append(parentElement, DOM.$('.default-preferences-editor-container'));
this.defaultPreferencesEditorContainer.style.position = 'absolute';
this.defaultPreferencesEditor = this._register(this.instantiationService.createInstance(DefaultPreferencesEditor));
this.defaultPreferencesEditor.create(new Builder(this.defaultPreferencesEditorContainer));
this.defaultPreferencesEditor.setVisible(true);
(<CodeEditor>this.defaultPreferencesEditor.getControl()).onDidFocusEditor(() => this.lastFocusedEditor = this.defaultPreferencesEditor);
this.editablePreferencesEditorContainer = DOM.append(parentElement, DOM.$('.editable-preferences-editor-container'));
this.editablePreferencesEditorContainer.style.position = 'absolute';
this._register(attachStylerCallback(this.themeService, { scrollbarShadow }, colors => {
const shadow = colors.scrollbarShadow ? colors.scrollbarShadow.toString() : null;
if (shadow) {
this.editablePreferencesEditorContainer.style.boxShadow = `-6px 0 5px -5px ${shadow}`;
} else {
this.editablePreferencesEditorContainer.style.boxShadow = null;
}
}));
const focusTracker = this._register(DOM.trackFocus(parentElement));
this._register(focusTracker.addFocusListener(() => this._onFocus.fire()));
}
public setInput(defaultPreferencesEditorInput: DefaultPreferencesEditorInput, editablePreferencesEditorInput: EditorInput, options?: EditorOptions): TPromise<{ defaultPreferencesRenderer: IPreferencesRenderer<ISetting>, editablePreferencesRenderer: IPreferencesRenderer<ISetting> }> {
return this.getOrCreateEditablePreferencesEditor(editablePreferencesEditorInput)
.then(() => {
this.dolayout(this.sash.getVerticalSashLeft());
return TPromise.join([this.updateInput(this.defaultPreferencesEditor, defaultPreferencesEditorInput, DefaultSettingsEditorContribution.ID, toResource(editablePreferencesEditorInput), options),
this.updateInput(this.editablePreferencesEditor, editablePreferencesEditorInput, SettingsEditorContribution.ID, defaultPreferencesEditorInput.getResource(), options)])
.then(([defaultPreferencesRenderer, editablePreferencesRenderer]) => ({ defaultPreferencesRenderer, editablePreferencesRenderer }));
});
}
public layout(dimension: Dimension): void {
this.dimension = dimension;
this.sash.setDimenesion(this.dimension);
}
public focus(): void {
if (this.lastFocusedEditor) {
this.lastFocusedEditor.focus();
}
}
public getControl(): IEditorControl {
return this.editablePreferencesEditor ? this.editablePreferencesEditor.getControl() : null;
}
public clearInput(): void {
if (this.editablePreferencesEditor) {
this.editablePreferencesEditor.clearInput();
}
}
public setEditorVisible(visible: boolean, position: Position): void {
if (this.editablePreferencesEditor) {
this.editablePreferencesEditor.setVisible(visible, position);
}
}
public changePosition(position: Position): void {
if (this.editablePreferencesEditor) {
this.editablePreferencesEditor.changePosition(position);
}
}
private getOrCreateEditablePreferencesEditor(editorInput: EditorInput): TPromise<BaseEditor> {
if (this.editablePreferencesEditor) {
return TPromise.as(this.editablePreferencesEditor);
}
const descriptor = Registry.as<IEditorRegistry>(EditorExtensions.Editors).getEditor(editorInput);
return this.instantiationService.createInstance(<EditorDescriptor>descriptor)
.then((editor: BaseEditor) => {
this.editablePreferencesEditor = editor;
this.editablePreferencesEditor.create(new Builder(this.editablePreferencesEditorContainer));
this.editablePreferencesEditor.setVisible(true);
(<CodeEditor>this.editablePreferencesEditor.getControl()).onDidFocusEditor(() => this.lastFocusedEditor = this.editablePreferencesEditor);
this.lastFocusedEditor = this.editablePreferencesEditor;
return editor;
});
}
private updateInput(editor: BaseEditor, input: EditorInput, editorContributionId: string, associatedPreferencesModelUri: URI, options: EditorOptions): TPromise<IPreferencesRenderer<ISetting>> {
return editor.setInput(input, options)
.then(() => (<CodeEditor>editor.getControl()).getContribution<ISettingsEditorContribution>(editorContributionId).updatePreferencesRenderer(associatedPreferencesModelUri));
}
private createSash(parentElement: HTMLElement): void {
this.sash = this._register(new VSash(parentElement, 220));
this._register(this.sash.onPositionChange(position => this.dolayout(position)));
}
private dolayout(splitPoint: number): void {
if (!this.editablePreferencesEditor || !this.dimension) {
return;
}
const masterEditorWidth = this.dimension.width - splitPoint;
const detailsEditorWidth = this.dimension.width - masterEditorWidth;
this.defaultPreferencesEditorContainer.style.width = `${detailsEditorWidth}px`;
this.defaultPreferencesEditorContainer.style.height = `${this.dimension.height}px`;
this.defaultPreferencesEditorContainer.style.left = '0px';
this.editablePreferencesEditorContainer.style.width = `${masterEditorWidth}px`;
this.editablePreferencesEditorContainer.style.height = `${this.dimension.height}px`;
this.editablePreferencesEditorContainer.style.left = `${splitPoint}px`;
this.defaultPreferencesEditor.layout(new Dimension(detailsEditorWidth, this.dimension.height));
this.editablePreferencesEditor.layout(new Dimension(masterEditorWidth, this.dimension.height));
}
private disposeEditors(): void {
if (this.defaultPreferencesEditor) {
this.defaultPreferencesEditor.dispose();
this.defaultPreferencesEditor = null;
}
if (this.editablePreferencesEditor) {
this.editablePreferencesEditor.dispose();
this.editablePreferencesEditor = null;
}
}
public dispose(): void {
this.disposeEditors();
super.dispose();
}
}
export class EditableSettingsEditor extends BaseTextEditor {
public static ID: string = 'workbench.editor.settingsEditor';
private modelDisposables: IDisposable[] = [];
private saveDelayer: Delayer<void>;
constructor(
@ITelemetryService telemetryService: ITelemetryService,
@IWorkbenchEditorService private editorService: IWorkbenchEditorService,
@IInstantiationService instantiationService: IInstantiationService,
@IStorageService storageService: IStorageService,
@ITextResourceConfigurationService configurationService: ITextResourceConfigurationService,
@IThemeService themeService: IThemeService,
@IPreferencesService private preferencesService: IPreferencesService,
@IModelService private modelService: IModelService,
@IModeService modeService: IModeService,
@ITextFileService textFileService: ITextFileService,
@IEditorGroupService editorGroupService: IEditorGroupService
) {
super(EditableSettingsEditor.ID, telemetryService, instantiationService, storageService, configurationService, themeService, modeService, textFileService, editorGroupService);
this._register({ dispose: () => dispose(this.modelDisposables) });
this.saveDelayer = new Delayer<void>(1000);
}
protected createEditor(parent: Builder): void {
super.createEditor(parent);
const codeEditor = getCodeEditor(this);
if (codeEditor) {
this._register(codeEditor.onDidChangeModel(() => this.onDidModelChange()));
}
}
protected getAriaLabel(): string {
const input = this.input;
const inputName = input && input.getName();
let ariaLabel: string;
if (inputName) {
ariaLabel = nls.localize('fileEditorWithInputAriaLabel', "{0}. Text file editor.", inputName);
} else {
ariaLabel = nls.localize('fileEditorAriaLabel', "Text file editor.");
}
return ariaLabel;
}
setInput(input: EditorInput, options: EditorOptions): TPromise<void> {
return super.setInput(input, options)
.then(() => this.input.resolve()
.then(editorModel => editorModel.load())
.then(editorModel => this.getControl().setModel((<ResourceEditorModel>editorModel).textEditorModel)));
}
clearInput(): void {
this.modelDisposables = dispose(this.modelDisposables);
super.clearInput();
}
private onDidModelChange(): void {
this.modelDisposables = dispose(this.modelDisposables);
const model = getCodeEditor(this).getModel();
if (model) {
this.preferencesService.createPreferencesEditorModel(model.uri)
.then(preferencesEditorModel => {
const settingsEditorModel = <SettingsEditorModel>preferencesEditorModel;
this.modelDisposables.push(settingsEditorModel);
this.modelDisposables.push(model.onDidChangeContent(() => this.saveDelayer.trigger(() => settingsEditorModel.save())));
});
}
}
}
export class DefaultPreferencesEditor extends BaseTextEditor {
public static ID: string = 'workbench.editor.defaultPreferences';
constructor(
@ITelemetryService telemetryService: ITelemetryService,
@IWorkbenchEditorService private editorService: IWorkbenchEditorService,
@IInstantiationService instantiationService: IInstantiationService,
@IStorageService storageService: IStorageService,
@ITextResourceConfigurationService configurationService: ITextResourceConfigurationService,
@IThemeService themeService: IThemeService,
@IPreferencesService private preferencesService: IPreferencesService,
@IModelService private modelService: IModelService,
@IModeService modeService: IModeService,
@ITextFileService textFileService: ITextFileService,
@IEditorGroupService editorGroupService: IEditorGroupService
) {
super(DefaultPreferencesEditor.ID, telemetryService, instantiationService, storageService, configurationService, themeService, modeService, textFileService, editorGroupService);
}
public createEditorControl(parent: Builder, configuration: IEditorOptions): editorCommon.IEditor {
return this.instantiationService.createInstance(DefaultPreferencesCodeEditor, parent.getHTMLElement(), configuration);
}
protected getConfigurationOverrides(): IEditorOptions {
const options = super.getConfigurationOverrides();
options.readOnly = true;
if (this.input) {
options.lineNumbers = 'off';
options.renderLineHighlight = 'none';
options.scrollBeyondLastLine = false;
options.folding = false;
options.renderWhitespace = 'none';
options.wordWrap = 'on';
options.renderIndentGuides = false;
options.rulers = [];
options.glyphMargin = true;
options.minimap = {
enabled: false
};
}
return options;
}
setInput(input: DefaultPreferencesEditorInput, options: EditorOptions): TPromise<void> {
return super.setInput(input, options)
.then(() => this.input.resolve()
.then(editorModel => editorModel.load())
.then(editorModel => this.getControl().setModel((<ResourceEditorModel>editorModel).textEditorModel)));
}
public layout(dimension: Dimension) {
this.getControl().layout(dimension);
}
protected getAriaLabel(): string {
return nls.localize('preferencesAriaLabel', "Default preferences. Readonly text editor.");
}
}
class DefaultPreferencesCodeEditor extends CodeEditor {
protected _getContributions(): IEditorContributionCtor[] {
let contributions = super._getContributions();
let skipContributions = [FoldingController.prototype, SelectionHighlighter.prototype, FindController.prototype];
contributions = contributions.filter(c => skipContributions.indexOf(c.prototype) === -1);
contributions.push(DefaultSettingsEditorContribution);
return contributions;
}
}
interface ISettingsEditorContribution extends editorCommon.IEditorContribution {
updatePreferencesRenderer(associatedPreferencesModelUri: URI): TPromise<IPreferencesRenderer<ISetting>>;
}
abstract class AbstractSettingsEditorContribution extends Disposable {
private preferencesRendererCreationPromise: TPromise<IPreferencesRenderer<ISetting>>;
constructor(protected editor: ICodeEditor,
@IInstantiationService protected instantiationService: IInstantiationService,
@IPreferencesService protected preferencesService: IPreferencesService,
@IWorkspaceContextService protected workspaceContextService: IWorkspaceContextService
) {
super();
this._register(this.editor.onDidChangeModel(() => this._onModelChanged()));
}
updatePreferencesRenderer(associatedPreferencesModelUri: URI): TPromise<IPreferencesRenderer<ISetting>> {
if (!this.preferencesRendererCreationPromise) {
this.preferencesRendererCreationPromise = this._createPreferencesRenderer();
}
if (this.preferencesRendererCreationPromise) {
return this._hasAssociatedPreferencesModelChanged(associatedPreferencesModelUri)
.then(changed => changed ? this._updatePreferencesRenderer(associatedPreferencesModelUri) : this.preferencesRendererCreationPromise);
}
return TPromise.as(null);
}
private _onModelChanged(): void {
const model = this.editor.getModel();
this.disposePreferencesRenderer();
if (model) {
this.preferencesRendererCreationPromise = this._createPreferencesRenderer();
}
}
private _hasAssociatedPreferencesModelChanged(associatedPreferencesModelUri: URI): TPromise<boolean> {
return this.preferencesRendererCreationPromise.then(preferencesRenderer => {
return !(preferencesRenderer && preferencesRenderer.associatedPreferencesModel && preferencesRenderer.associatedPreferencesModel.uri.fsPath === associatedPreferencesModelUri.fsPath);
});
}
private _updatePreferencesRenderer(associatedPreferencesModelUri: URI): TPromise<IPreferencesRenderer<ISetting>> {
return this.preferencesService.createPreferencesEditorModel<ISetting>(associatedPreferencesModelUri)
.then(associatedPreferencesEditorModel => {
return this.preferencesRendererCreationPromise.then(preferencesRenderer => {
if (preferencesRenderer) {
if (preferencesRenderer.associatedPreferencesModel) {
preferencesRenderer.associatedPreferencesModel.dispose();
}
preferencesRenderer.associatedPreferencesModel = associatedPreferencesEditorModel;
}
return preferencesRenderer;
});
});
}
private disposePreferencesRenderer(): void {
if (this.preferencesRendererCreationPromise) {
this.preferencesRendererCreationPromise.then(preferencesRenderer => {
if (preferencesRenderer) {
if (preferencesRenderer.associatedPreferencesModel) {
preferencesRenderer.associatedPreferencesModel.dispose();
}
preferencesRenderer.dispose();
}
});
this.preferencesRendererCreationPromise = TPromise.as(null);
}
}
dispose() {
this.disposePreferencesRenderer();
super.dispose();
}
protected abstract _createPreferencesRenderer(): TPromise<IPreferencesRenderer<ISetting>>;
}
class DefaultSettingsEditorContribution extends AbstractSettingsEditorContribution implements ISettingsEditorContribution {
static ID: string = 'editor.contrib.defaultsettings';
getId(): string {
return DefaultSettingsEditorContribution.ID;
}
protected _createPreferencesRenderer(): TPromise<IPreferencesRenderer<ISetting>> {
return this.preferencesService.createPreferencesEditorModel(this.editor.getModel().uri)
.then(editorModel => {
if (editorModel instanceof DefaultSettingsEditorModel && this.editor.getModel()) {
const preferencesRenderer = this.instantiationService.createInstance(DefaultSettingsRenderer, this.editor, editorModel);
preferencesRenderer.render();
return preferencesRenderer;
}
return null;
});
}
}
@editorContribution
class SettingsEditorContribution extends AbstractSettingsEditorContribution implements ISettingsEditorContribution {
static ID: string = 'editor.contrib.settings';
getId(): string {
return SettingsEditorContribution.ID;
}
protected _createPreferencesRenderer(): TPromise<IPreferencesRenderer<ISetting>> {
if (this.isSettingsModel()) {
return TPromise.join<any>([this.preferencesService.createPreferencesEditorModel(this.preferencesService.defaultSettingsResource), this.preferencesService.createPreferencesEditorModel(this.editor.getModel().uri)])
.then(([defaultSettingsModel, settingsModel]) => {
if (settingsModel instanceof SettingsEditorModel && this.editor.getModel()) {
switch (settingsModel.configurationTarget) {
case ConfigurationTarget.USER:
return this.instantiationService.createInstance(UserSettingsRenderer, this.editor, settingsModel, defaultSettingsModel);
case ConfigurationTarget.WORKSPACE:
return this.instantiationService.createInstance(WorkspaceSettingsRenderer, this.editor, settingsModel, defaultSettingsModel);
case ConfigurationTarget.FOLDER:
return this.instantiationService.createInstance(FolderSettingsRenderer, this.editor, settingsModel, defaultSettingsModel);
}
}
return null;
})
.then(preferencesRenderer => {
if (preferencesRenderer) {
preferencesRenderer.render();
}
return preferencesRenderer;
});
}
return null;
}
private isSettingsModel(): boolean {
const model = this.editor.getModel();
if (!model) {
return false;
}
if (this.preferencesService.userSettingsResource && this.preferencesService.userSettingsResource.fsPath === model.uri.fsPath) {
return true;
}
if (this.preferencesService.workspaceSettingsResource && this.preferencesService.workspaceSettingsResource.fsPath === model.uri.fsPath) {
return true;
}
const workspace = this.workspaceContextService.getWorkspace();
if (workspace) {
for (const root of workspace.roots) {
const folderSettingsResource = this.preferencesService.getFolderSettingsResource(root);
if (folderSettingsResource && folderSettingsResource.fsPath === model.uri.fsPath) {
return true;
}
}
}
return false;
}
}
abstract class SettingsCommand extends Command {
protected getPreferencesEditor(accessor: ServicesAccessor): PreferencesEditor {
const activeEditor = accessor.get(IWorkbenchEditorService).getActiveEditor();
if (activeEditor instanceof PreferencesEditor) {
return activeEditor;
}
return null;
}
}
class StartSearchDefaultSettingsCommand extends SettingsCommand {
public runCommand(accessor: ServicesAccessor, args: any): void {
const preferencesEditor = this.getPreferencesEditor(accessor);
if (preferencesEditor) {
preferencesEditor.focusSearch();
}
}
}
const command = new StartSearchDefaultSettingsCommand({
id: SETTINGS_EDITOR_COMMAND_SEARCH,
precondition: ContextKeyExpr.and(CONTEXT_SETTINGS_EDITOR),
kbOpts: { primary: KeyMod.CtrlCmd | KeyCode.KEY_F }
});
KeybindingsRegistry.registerCommandAndKeybindingRule(command.toCommandAndKeybindingRule(KeybindingsRegistry.WEIGHT.editorContrib()));
class FocusSettingsFileEditorCommand extends SettingsCommand {
public runCommand(accessor: ServicesAccessor, args: any): void {
const preferencesEditor = this.getPreferencesEditor(accessor);
if (preferencesEditor) {
preferencesEditor.focusSettingsFileEditor();
}
}
}
const focusSettingsFileEditorCommand = new FocusSettingsFileEditorCommand({
id: SETTINGS_EDITOR_COMMAND_FOCUS_FILE,
precondition: CONTEXT_SETTINGS_SEARCH_FOCUS,
kbOpts: { primary: KeyCode.DownArrow }
});
KeybindingsRegistry.registerCommandAndKeybindingRule(focusSettingsFileEditorCommand.toCommandAndKeybindingRule(KeybindingsRegistry.WEIGHT.editorContrib()));

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,422 @@
/*---------------------------------------------------------------------------------------------
* 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/preferences';
import * as network from 'vs/base/common/network';
import { TPromise } from 'vs/base/common/winjs.base';
import * as nls from 'vs/nls';
import URI from 'vs/base/common/uri';
import * as paths from 'vs/base/common/paths';
import { ResourceMap } from 'vs/base/common/map';
import * as labels from 'vs/base/common/labels';
import * as strings from 'vs/base/common/strings';
import { Disposable } from 'vs/base/common/lifecycle';
import { Emitter } from 'vs/base/common/event';
import { EditorInput } from 'vs/workbench/common/editor';
import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService';
import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
import { IWorkspaceConfigurationService } from 'vs/workbench/services/configuration/common/configuration';
import { Position as EditorPosition, IEditor } from 'vs/platform/editor/common/editor';
import { ICommonCodeEditor } from 'vs/editor/common/editorCommon';
import { IEditorGroupService } from 'vs/workbench/services/group/common/groupService';
import { IStorageService } from 'vs/platform/storage/common/storage';
import { IFileService, FileOperationError, FileOperationResult } from 'vs/platform/files/common/files';
import { IMessageService, Severity, IChoiceService } from 'vs/platform/message/common/message';
import { IExtensionService } from 'vs/platform/extensions/common/extensions';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { IConfigurationEditingService, ConfigurationTarget } from 'vs/workbench/services/configuration/common/configurationEditing';
import { IPreferencesService, IPreferencesEditorModel, ISetting, getSettingsTargetName } from 'vs/workbench/parts/preferences/common/preferences';
import { SettingsEditorModel, DefaultSettingsEditorModel, DefaultKeybindingsEditorModel, defaultKeybindingsContents, WorkspaceConfigModel } from 'vs/workbench/parts/preferences/common/preferencesModels';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { DefaultPreferencesEditorInput, PreferencesEditorInput } from 'vs/workbench/parts/preferences/browser/preferencesEditor';
import { KeybindingsEditorInput } from 'vs/workbench/parts/preferences/browser/keybindingsEditor';
import { ITextModelService } from 'vs/editor/common/services/resolverService';
import { getCodeEditor } from 'vs/editor/common/services/codeEditorService';
import { EditOperation } from 'vs/editor/common/core/editOperation';
import { Position, IPosition } from 'vs/editor/common/core/position';
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
import { IModelService } from 'vs/editor/common/services/modelService';
import { IJSONEditingService } from 'vs/workbench/services/configuration/common/jsonEditing';
import { ConfigurationScope } from 'vs/platform/configuration/common/configurationRegistry';
interface IWorkbenchSettingsConfiguration {
workbench: {
settings: {
openDefaultSettings: boolean;
}
};
}
const emptyEditableSettingsContent = '{\n}';
export class PreferencesService extends Disposable implements IPreferencesService {
_serviceBrand: any;
// TODO:@sandy merge these models into editor inputs by extending resource editor model
private defaultPreferencesEditorModels: ResourceMap<TPromise<IPreferencesEditorModel<any>>>;
private lastOpenedSettingsInput: PreferencesEditorInput = null;
private _onDispose: Emitter<void> = new Emitter<void>();
constructor(
@IWorkbenchEditorService private editorService: IWorkbenchEditorService,
@IEditorGroupService private editorGroupService: IEditorGroupService,
@IFileService private fileService: IFileService,
@IWorkspaceConfigurationService private configurationService: IWorkspaceConfigurationService,
@IMessageService private messageService: IMessageService,
@IChoiceService private choiceService: IChoiceService,
@IWorkspaceContextService private contextService: IWorkspaceContextService,
@IInstantiationService private instantiationService: IInstantiationService,
@IStorageService private storageService: IStorageService,
@IEnvironmentService private environmentService: IEnvironmentService,
@ITelemetryService private telemetryService: ITelemetryService,
@ITextModelService private textModelResolverService: ITextModelService,
@IConfigurationEditingService private configurationEditingService: IConfigurationEditingService,
@IExtensionService private extensionService: IExtensionService,
@IKeybindingService keybindingService: IKeybindingService,
@IModelService private modelService: IModelService,
@IJSONEditingService private jsonEditingService: IJSONEditingService
) {
super();
this.defaultPreferencesEditorModels = new ResourceMap<TPromise<IPreferencesEditorModel<any>>>();
this.editorGroupService.onEditorsChanged(() => {
const activeEditorInput = this.editorService.getActiveEditorInput();
if (activeEditorInput instanceof PreferencesEditorInput) {
this.lastOpenedSettingsInput = activeEditorInput;
}
});
// The default keybindings.json updates based on keyboard layouts, so here we make sure
// if a model has been given out we update it accordingly.
keybindingService.onDidUpdateKeybindings(() => {
const model = modelService.getModel(this.defaultKeybindingsResource);
if (!model) {
// model has not been given out => nothing to do
return;
}
modelService.updateModel(model, defaultKeybindingsContents(keybindingService));
});
}
readonly defaultSettingsResource = URI.from({ scheme: network.Schemas.vscode, authority: 'defaultsettings', path: '/settings.json' });
readonly defaultResourceSettingsResource = URI.from({ scheme: network.Schemas.vscode, authority: 'defaultsettings', path: '/resourceSettings.json' });
readonly defaultKeybindingsResource = URI.from({ scheme: network.Schemas.vscode, authority: 'defaultsettings', path: '/keybindings.json' });
private readonly workspaceConfigSettingsResource = URI.from({ scheme: network.Schemas.vscode, authority: 'settings', path: '/workspaceSettings.json' });
get userSettingsResource(): URI {
return this.getEditableSettingsURI(ConfigurationTarget.USER);
}
get workspaceSettingsResource(): URI {
return this.getEditableSettingsURI(ConfigurationTarget.WORKSPACE);
}
getFolderSettingsResource(resource: URI): URI {
return this.getEditableSettingsURI(ConfigurationTarget.FOLDER, resource);
}
resolveContent(uri: URI): TPromise<string> {
const workspaceSettingsUri = this.getEditableSettingsURI(ConfigurationTarget.WORKSPACE);
if (workspaceSettingsUri && workspaceSettingsUri.fsPath === uri.fsPath) {
return this.resolveSettingsContentFromWorkspaceConfiguration();
}
return this.createPreferencesEditorModel(uri)
.then(preferencesEditorModel => preferencesEditorModel ? preferencesEditorModel.content : null);
}
createPreferencesEditorModel(uri: URI): TPromise<IPreferencesEditorModel<any>> {
let promise = this.defaultPreferencesEditorModels.get(uri);
if (promise) {
return promise;
}
if (this.defaultSettingsResource.fsPath === uri.fsPath) {
promise = TPromise.join<any>([this.extensionService.onReady(), this.fetchMostCommonlyUsedSettings()])
.then(result => {
const mostCommonSettings = result[1];
const model = this.instantiationService.createInstance(DefaultSettingsEditorModel, uri, mostCommonSettings, ConfigurationScope.WINDOW);
return model;
});
this.defaultPreferencesEditorModels.set(uri, promise);
return promise;
}
if (this.defaultResourceSettingsResource.fsPath === uri.fsPath) {
promise = TPromise.join<any>([this.extensionService.onReady(), this.fetchMostCommonlyUsedSettings()])
.then(result => {
const mostCommonSettings = result[1];
const model = this.instantiationService.createInstance(DefaultSettingsEditorModel, uri, mostCommonSettings, ConfigurationScope.RESOURCE);
return model;
});
this.defaultPreferencesEditorModels.set(uri, promise);
return promise;
}
if (this.defaultKeybindingsResource.fsPath === uri.fsPath) {
const model = this.instantiationService.createInstance(DefaultKeybindingsEditorModel, uri);
promise = TPromise.wrap(model);
this.defaultPreferencesEditorModels.set(uri, promise);
return promise;
}
if (this.workspaceConfigSettingsResource.fsPath === uri.fsPath) {
promise = this.createEditableSettingsEditorModel(ConfigurationTarget.WORKSPACE, uri);
this.defaultPreferencesEditorModels.set(uri, promise);
return promise;
}
if (this.getEditableSettingsURI(ConfigurationTarget.USER).fsPath === uri.fsPath) {
return this.createEditableSettingsEditorModel(ConfigurationTarget.USER, uri);
}
const workspaceSettingsUri = this.getEditableSettingsURI(ConfigurationTarget.WORKSPACE);
if (workspaceSettingsUri && workspaceSettingsUri.fsPath === uri.fsPath) {
return this.createEditableSettingsEditorModel(ConfigurationTarget.WORKSPACE, workspaceSettingsUri);
}
if (this.contextService.hasMultiFolderWorkspace()) {
return this.createEditableSettingsEditorModel(ConfigurationTarget.FOLDER, uri);
}
return TPromise.wrap<IPreferencesEditorModel<any>>(null);
}
openGlobalSettings(): TPromise<IEditor> {
return this.doOpenSettings(ConfigurationTarget.USER, this.userSettingsResource);
}
openWorkspaceSettings(): TPromise<IEditor> {
if (!this.contextService.hasWorkspace()) {
this.messageService.show(Severity.Info, nls.localize('openFolderFirst', "Open a folder first to create workspace settings"));
return TPromise.as(null);
}
return this.doOpenSettings(ConfigurationTarget.WORKSPACE, this.workspaceSettingsResource);
}
openFolderSettings(folder: URI): TPromise<IEditor> {
return this.doOpenSettings(ConfigurationTarget.FOLDER, this.getEditableSettingsURI(ConfigurationTarget.FOLDER, folder));
}
switchSettings(target: ConfigurationTarget, resource: URI): TPromise<void> {
const activeEditor = this.editorService.getActiveEditor();
const activeEditorInput = activeEditor.input;
if (activeEditorInput instanceof PreferencesEditorInput) {
return this.getOrCreateEditableSettingsEditorInput(target, this.getEditableSettingsURI(target, resource))
.then(toInput => {
const replaceWith = new PreferencesEditorInput(this.getPreferencesEditorInputName(target, resource), toInput.getDescription(), this.instantiationService.createInstance(DefaultPreferencesEditorInput, this.getDefaultSettingsResource(target)), toInput);
return this.editorService.replaceEditors([{
toReplace: this.lastOpenedSettingsInput,
replaceWith
}], activeEditor.position).then(() => {
this.lastOpenedSettingsInput = replaceWith;
});
});
} else {
this.doOpenSettings(target, resource);
return undefined;
}
}
openGlobalKeybindingSettings(textual: boolean): TPromise<void> {
this.telemetryService.publicLog('openKeybindings', { textual });
if (textual) {
const emptyContents = '// ' + nls.localize('emptyKeybindingsHeader', "Place your key bindings in this file to overwrite the defaults") + '\n[\n]';
const editableKeybindings = URI.file(this.environmentService.appKeybindingsPath);
// Create as needed and open in editor
return this.createIfNotExists(editableKeybindings, emptyContents).then(() => {
return this.editorService.openEditors([
{ input: { resource: this.defaultKeybindingsResource, options: { pinned: true }, label: nls.localize('defaultKeybindings', "Default Keybindings"), description: '' }, position: EditorPosition.ONE },
{ input: { resource: editableKeybindings, options: { pinned: true } }, position: EditorPosition.TWO },
]).then(() => {
this.editorGroupService.focusGroup(EditorPosition.TWO);
});
});
}
return this.editorService.openEditor(this.instantiationService.createInstance(KeybindingsEditorInput), { pinned: true }).then(() => null);
}
configureSettingsForLanguage(language: string): void {
this.openGlobalSettings()
.then(editor => {
const codeEditor = getCodeEditor(editor);
this.getPosition(language, codeEditor)
.then(position => {
codeEditor.setPosition(position);
codeEditor.focus();
});
});
}
private doOpenSettings(configurationTarget: ConfigurationTarget, resource: URI): TPromise<IEditor> {
const openDefaultSettings = !!this.configurationService.getConfiguration<IWorkbenchSettingsConfiguration>().workbench.settings.openDefaultSettings;
return this.getOrCreateEditableSettingsEditorInput(configurationTarget, resource)
.then(editableSettingsEditorInput => {
if (openDefaultSettings) {
const defaultPreferencesEditorInput = this.instantiationService.createInstance(DefaultPreferencesEditorInput, this.getDefaultSettingsResource(configurationTarget));
const preferencesEditorInput = new PreferencesEditorInput(this.getPreferencesEditorInputName(configurationTarget, resource), editableSettingsEditorInput.getDescription(), defaultPreferencesEditorInput, <EditorInput>editableSettingsEditorInput);
this.lastOpenedSettingsInput = preferencesEditorInput;
return this.editorService.openEditor(preferencesEditorInput, { pinned: true });
}
return this.editorService.openEditor(editableSettingsEditorInput, { pinned: true });
});
}
private getDefaultSettingsResource(configurationTarget: ConfigurationTarget): URI {
if (configurationTarget === ConfigurationTarget.FOLDER) {
return this.defaultResourceSettingsResource;
}
return this.defaultSettingsResource;
}
private getPreferencesEditorInputName(target: ConfigurationTarget, resource: URI): string {
const name = getSettingsTargetName(target, resource, this.contextService);
return target === ConfigurationTarget.FOLDER ? nls.localize('folderSettingsName', "{0} (Folder Settings)", name) : name;
}
private getOrCreateEditableSettingsEditorInput(target: ConfigurationTarget, resource: URI): TPromise<EditorInput> {
return this.createSettingsIfNotExists(target, resource)
.then(() => <EditorInput>this.editorService.createInput({ resource }));
}
private createEditableSettingsEditorModel(configurationTarget: ConfigurationTarget, resource: URI): TPromise<SettingsEditorModel> {
const settingsUri = this.getEditableSettingsURI(configurationTarget, resource);
if (settingsUri) {
if (settingsUri.fsPath === this.workspaceConfigSettingsResource.fsPath) {
return TPromise.join([this.textModelResolverService.createModelReference(settingsUri), this.textModelResolverService.createModelReference(this.contextService.getWorkspace().configuration)])
.then(([reference, workspaceConfigReference]) => this.instantiationService.createInstance(WorkspaceConfigModel, reference, workspaceConfigReference, configurationTarget, this._onDispose.event));
}
return this.textModelResolverService.createModelReference(settingsUri)
.then(reference => this.instantiationService.createInstance(SettingsEditorModel, reference, configurationTarget));
}
return TPromise.wrap<SettingsEditorModel>(null);
}
private resolveSettingsContentFromWorkspaceConfiguration(): TPromise<string> {
if (this.contextService.hasMultiFolderWorkspace()) {
return this.textModelResolverService.createModelReference(this.contextService.getWorkspace().configuration)
.then(reference => {
const model = reference.object.textEditorModel;
const settingsContent = WorkspaceConfigModel.getSettingsContentFromConfigContent(model.getValue());
reference.dispose();
return TPromise.as(settingsContent ? settingsContent : emptyEditableSettingsContent);
});
}
return TPromise.as(null);
}
private getEditableSettingsURI(configurationTarget: ConfigurationTarget, resource?: URI): URI {
switch (configurationTarget) {
case ConfigurationTarget.USER:
return URI.file(this.environmentService.appSettingsPath);
case ConfigurationTarget.WORKSPACE:
const workspace = this.contextService.getWorkspace();
if (this.contextService.hasFolderWorkspace()) {
// {{SQL CARBON EDIT}}
return this.toResource(paths.join('.sqlops', 'settings.json'), workspace.roots[0]);
}
if (this.contextService.hasMultiFolderWorkspace()) {
return workspace.configuration;
}
return null;
case ConfigurationTarget.FOLDER:
const root = this.contextService.getRoot(resource);
// {{SQL CARBON EDIT}}
return root ? this.toResource(paths.join('.sqlops', 'settings.json'), root) : null;
}
return null;
}
private toResource(relativePath: string, root: URI): URI {
return URI.file(paths.join(root.fsPath, relativePath));
}
private createSettingsIfNotExists(target: ConfigurationTarget, resource: URI): TPromise<void> {
if (this.contextService.hasMultiFolderWorkspace() && target === ConfigurationTarget.WORKSPACE) {
if (!this.configurationService.keys().workspace.length) {
return this.jsonEditingService.write(resource, { key: 'settings', value: {} }, true).then(null, () => { });
}
}
return this.createIfNotExists(resource, emptyEditableSettingsContent).then(() => { });
}
private createIfNotExists(resource: URI, contents: string): TPromise<any> {
return this.fileService.resolveContent(resource, { acceptTextOnly: true }).then(null, error => {
if ((<FileOperationError>error).fileOperationResult === FileOperationResult.FILE_NOT_FOUND) {
return this.fileService.updateContent(resource, contents).then(null, error => {
return TPromise.wrapError(new Error(nls.localize('fail.createSettings', "Unable to create '{0}' ({1}).", labels.getPathLabel(resource, this.contextService, this.environmentService), error)));
});
}
return TPromise.wrapError(error);
});
}
private fetchMostCommonlyUsedSettings(): TPromise<string[]> {
return TPromise.wrap([
'files.autoSave',
'editor.fontSize',
'editor.fontFamily',
'editor.tabSize',
'editor.renderWhitespace',
'editor.cursorStyle',
'editor.multiCursorModifier',
'editor.insertSpaces',
'editor.wordWrap',
'files.exclude',
'files.associations'
]);
}
private getPosition(language: string, codeEditor: ICommonCodeEditor): TPromise<IPosition> {
return this.createPreferencesEditorModel(this.userSettingsResource)
.then((settingsModel: IPreferencesEditorModel<ISetting>) => {
const languageKey = `[${language}]`;
let setting = settingsModel.getPreference(languageKey);
const model = codeEditor.getModel();
const configuration = this.configurationService.getConfiguration<{ tabSize: number; insertSpaces: boolean }>('editor');
const { eol } = this.configurationService.getConfiguration<{ eol: string }>('files');
if (setting) {
if (setting.overrides.length) {
const lastSetting = setting.overrides[setting.overrides.length - 1];
let content;
if (lastSetting.valueRange.endLineNumber === setting.range.endLineNumber) {
content = ',' + eol + this.spaces(2, configuration) + eol + this.spaces(1, configuration);
} else {
content = ',' + eol + this.spaces(2, configuration);
}
const editOperation = EditOperation.insert(new Position(lastSetting.valueRange.endLineNumber, lastSetting.valueRange.endColumn), content);
model.pushEditOperations([], [editOperation], () => []);
return { lineNumber: lastSetting.valueRange.endLineNumber + 1, column: model.getLineMaxColumn(lastSetting.valueRange.endLineNumber + 1) };
}
return { lineNumber: setting.valueRange.startLineNumber, column: setting.valueRange.startColumn + 1 };
}
return this.configurationEditingService.writeConfiguration(ConfigurationTarget.USER, { key: languageKey, value: {} }, { donotSave: true })
.then(() => {
setting = settingsModel.getPreference(languageKey);
let content = eol + this.spaces(2, configuration) + eol + this.spaces(1, configuration);
let editOperation = EditOperation.insert(new Position(setting.valueRange.endLineNumber, setting.valueRange.endColumn - 1), content);
model.pushEditOperations([], [editOperation], () => []);
let lineNumber = setting.valueRange.endLineNumber + 1;
settingsModel.dispose();
return { lineNumber, column: model.getLineMaxColumn(lineNumber) };
});
});
}
private spaces(count: number, { tabSize, insertSpaces }: { tabSize: number; insertSpaces: boolean }): string {
return insertSpaces ? strings.repeat(' ', tabSize * count) : strings.repeat('\t', count);
}
public dispose(): void {
this._onDispose.fire();
this.defaultPreferencesEditorModels.clear();
super.dispose();
}
}

View File

@@ -0,0 +1,652 @@
/*---------------------------------------------------------------------------------------------
* 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 URI from 'vs/base/common/uri';
import { Dimension } from 'vs/base/browser/builder';
import * as DOM from 'vs/base/browser/dom';
import { TPromise } from 'vs/base/common/winjs.base';
import { Disposable } from 'vs/base/common/lifecycle';
import { Widget } from 'vs/base/browser/ui/widget';
import Event, { Emitter } from 'vs/base/common/event';
import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent';
import { KeyCode } from 'vs/base/common/keyCodes';
import { ICodeEditor, IOverlayWidget, IOverlayWidgetPosition, OverlayWidgetPositionPreference, IViewZone, IEditorMouseEvent, MouseTargetType } from 'vs/editor/browser/editorBrowser';
import * as editorCommon from 'vs/editor/common/editorCommon';
import { InputBox, IInputOptions } from 'vs/base/browser/ui/inputbox/inputBox';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IContextViewService, IContextMenuService } from 'vs/platform/contextview/browser/contextView';
import { ISettingsGroup, IPreferencesService, getSettingsTargetName } from 'vs/workbench/parts/preferences/common/preferences';
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
import { IAction, IActionRunner } from 'vs/base/common/actions';
import { attachInputBoxStyler, attachStylerCallback, attachSelectBoxStyler } from 'vs/platform/theme/common/styler';
import { IThemeService } from 'vs/platform/theme/common/themeService';
import { Position } from 'vs/editor/common/core/position';
import { ICursorPositionChangedEvent } from 'vs/editor/common/controller/cursorEvents';
import { buttonBackground, buttonForeground, badgeForeground, badgeBackground, contrastBorder, errorForeground } from 'vs/platform/theme/common/colorRegistry';
import { IContextKey } from 'vs/platform/contextkey/common/contextkey';
import { ISelectBoxStyles, defaultStyles } from 'vs/base/browser/ui/selectBox/selectBox';
import { Separator } from 'vs/base/browser/ui/actionbar/actionbar';
import { Color } from 'vs/base/common/color';
import { SIDE_BAR_BACKGROUND } from 'vs/workbench/common/theme';
import { ConfigurationTarget } from 'vs/workbench/services/configuration/common/configurationEditing';
import { IMouseEvent } from 'vs/base/browser/mouseEvent';
import { MarkdownString } from 'vs/base/common/htmlContent';
export class SettingsHeaderWidget extends Widget implements IViewZone {
private id: number;
private _domNode: HTMLElement;
private titleContainer: HTMLElement;
private messageElement: HTMLElement;
constructor(private 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;
}
private 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();
});
}
public 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 = '12px';
}
}
public dispose() {
this.editor.changeViewZones(accessor => {
accessor.removeZone(this.id);
});
super.dispose();
}
}
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>());
public 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));
focusTracker.addFocusListener(() => this.toggleFocus(true));
focusTracker.addBlurListener(() => 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();
}
public render() {
this._afterLineNumber = this.settingsGroup.range.startLineNumber - 2;
this.editor.changeViewZones(accessor => {
this.id = accessor.addZone(this);
this.layout();
});
}
public toggleCollapse(collapse: boolean) {
DOM.toggleClass(this.titleContainer, 'collapsed', collapse);
}
public toggleFocus(focus: boolean): void {
DOM.toggleClass(this.titleContainer, 'focused', focus);
}
public 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;
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();
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 (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;
}
public dispose() {
this.editor.changeViewZones(accessor => {
accessor.removeZone(this.id);
});
super.dispose();
}
}
export class SettingsTargetsWidget extends Widget {
public actionRunner: IActionRunner;
private settingsTargetsContainer: HTMLSelectElement;
private targetLabel: HTMLSelectElement;
private targetDetails: HTMLSelectElement;
private _onDidTargetChange: Emitter<URI> = new Emitter<URI>();
public readonly onDidTargetChange: Event<URI> = this._onDidTargetChange.event;
private borderColor: Color;
constructor(parent: HTMLElement, private uri: URI, private target: ConfigurationTarget,
@IWorkspaceContextService private workspaceContextService: IWorkspaceContextService,
@IPreferencesService private preferencesService: IPreferencesService,
@IContextMenuService private contextMenuService: IContextMenuService,
@IThemeService themeService: IThemeService) {
super();
this.borderColor = defaultStyles.selectBorder;
this.create(parent);
this._register(attachSelectBoxStyler(this, themeService, {
selectBackground: SIDE_BAR_BACKGROUND
}));
}
public setTarget(uri: URI, target: ConfigurationTarget): void {
this.uri = uri;
this.target = target;
this.updateLabel();
}
private create(parent: HTMLElement): void {
this.settingsTargetsContainer = DOM.append(parent, DOM.$('.settings-targets-widget'));
this.settingsTargetsContainer.style.width = this.workspaceContextService.hasMultiFolderWorkspace() ? '200px' : '150px';
const targetElement = DOM.append(this.settingsTargetsContainer, DOM.$('.settings-target'));
this.targetLabel = DOM.append(targetElement, DOM.$('.settings-target-label'));
this.targetDetails = DOM.append(targetElement, DOM.$('.settings-target-details'));
this.updateLabel();
this.onclick(this.settingsTargetsContainer, e => this.showContextMenu(e));
DOM.append(this.settingsTargetsContainer, DOM.$('.settings-target-dropdown-icon.octicon.octicon-triangle-down'));
this.applyStyles();
}
private updateLabel(): void {
this.targetLabel.textContent = getSettingsTargetName(this.target, this.uri, this.workspaceContextService);
const details = ConfigurationTarget.FOLDER === this.target ? localize('folderSettingsDetails', "Folder Settings") : '';
this.targetDetails.textContent = details;
DOM.toggleClass(this.targetDetails, 'empty', !details);
}
private showContextMenu(event: IMouseEvent): void {
const actions = this.getSettingsTargetsActions();
let elementPosition = DOM.getDomNodePagePosition(this.settingsTargetsContainer);
const anchor = { x: elementPosition.left, y: elementPosition.top + elementPosition.height + 5 };
this.contextMenuService.showContextMenu({
getAnchor: () => anchor,
getActions: () => TPromise.wrap(actions)
});
event.stopPropagation();
event.preventDefault();
}
private getSettingsTargetsActions(): IAction[] {
const actions: IAction[] = [];
const userSettingsResource = this.preferencesService.userSettingsResource;
actions.push(<IAction>{
id: 'userSettingsTarget',
label: getSettingsTargetName(ConfigurationTarget.USER, userSettingsResource, this.workspaceContextService),
checked: this.uri.fsPath === userSettingsResource.fsPath,
enabled: true,
run: () => this.onTargetClicked(userSettingsResource)
});
if (this.workspaceContextService.hasWorkspace()) {
const workspaceSettingsResource = this.preferencesService.workspaceSettingsResource;
actions.push(<IAction>{
id: 'workspaceSettingsTarget',
label: getSettingsTargetName(ConfigurationTarget.WORKSPACE, workspaceSettingsResource, this.workspaceContextService),
checked: this.uri.fsPath === workspaceSettingsResource.fsPath,
enabled: true,
run: () => this.onTargetClicked(workspaceSettingsResource)
});
}
if (this.workspaceContextService.hasMultiFolderWorkspace()) {
actions.push(new Separator());
actions.push(...this.workspaceContextService.getWorkspace().roots.map((root, index) => {
return <IAction>{
id: 'folderSettingsTarget' + index,
label: getSettingsTargetName(ConfigurationTarget.FOLDER, root, this.workspaceContextService),
checked: this.uri.fsPath === root.fsPath,
enabled: true,
run: () => this.onTargetClicked(root)
};
}));
}
return actions;
}
private onTargetClicked(target: URI): void {
if (this.uri.fsPath === target.fsPath) {
return;
}
this._onDidTargetChange.fire(target);
}
style(styles: ISelectBoxStyles): void {
this.borderColor = styles.selectBorder;
this.applyStyles();
}
private applyStyles(): void {
if (this.settingsTargetsContainer) {
this.settingsTargetsContainer.style.border = this.borderColor ? `1px solid ${this.borderColor}` : null;
}
}
}
export interface SearchOptions extends IInputOptions {
focusKey?: IContextKey<boolean>;
}
export class SearchWidget extends Widget {
public domNode: HTMLElement;
private countElement: HTMLElement;
private searchContainer: HTMLElement;
private inputBox: InputBox;
private _onDidChange: Emitter<string> = this._register(new Emitter<string>());
public readonly onDidChange: Event<string> = this._onDidChange.event;
private _onNavigate: Emitter<boolean> = this._register(new Emitter<boolean>());
public readonly onNavigate: Event<boolean> = this._onNavigate.event;
private _onFocus: Emitter<void> = this._register(new Emitter<void>());
public readonly onFocus: Event<void> = this._onFocus.event;
constructor(parent: HTMLElement, protected options: SearchOptions,
@IContextViewService private contextViewService: IContextViewService,
@IContextMenuService private contextMenuService: IContextMenuService,
@IInstantiationService protected instantiationService: IInstantiationService,
@IThemeService private 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.countElement = DOM.append(this.domNode, 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;
this.styleCountElementForeground();
}));
this.inputBox.inputElement.setAttribute('aria-live', 'assertive');
const focusTracker = this._register(DOM.trackFocus(this.inputBox.inputElement));
this._register(focusTracker.addFocusListener(() => this._onFocus.fire()));
if (this.options.focusKey) {
this._register(focusTracker.addFocusListener(() => this.options.focusKey.set(true)));
this._register(focusTracker.addBlurListener(() => this.options.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)));
this.onkeydown(this.inputBox.inputElement, (e) => this._onKeyDown(e));
}
protected createInputBox(parent: HTMLElement): InputBox {
const box = this._register(new InputBox(parent, this.contextViewService, this.options));
this._register(attachInputBoxStyler(box, this.themeService));
return box;
}
public showMessage(message: string, count: number): void {
this.countElement.textContent = message;
this.inputBox.inputElement.setAttribute('aria-label', message);
DOM.toggleClass(this.countElement, 'no-results', count === 0);
this.inputBox.inputElement.style.paddingRight = DOM.getTotalWidth(this.countElement) + 20 + 'px';
this.styleCountElementForeground();
}
private styleCountElementForeground() {
const colorId = DOM.hasClass(this.countElement, 'no-results') ? errorForeground : badgeForeground;
const color = this.themeService.getTheme().getColor(colorId);
this.countElement.style.color = color ? color.toString() : null;
}
public layout(dimension: Dimension) {
if (dimension.width < 400) {
DOM.addClass(this.countElement, 'hide');
this.inputBox.inputElement.style.paddingRight = '0px';
} else {
DOM.removeClass(this.countElement, 'hide');
this.inputBox.inputElement.style.paddingRight = DOM.getTotalWidth(this.countElement) + 20 + 'px';
}
}
public focus() {
this.inputBox.focus();
if (this.getValue()) {
this.inputBox.select();
}
}
public hasFocus(): boolean {
return this.inputBox.hasFocus();
}
public clear() {
this.inputBox.value = '';
}
public getValue(): string {
return this.inputBox.value;
}
public setValue(value: string): string {
return this.inputBox.value = value;
}
private _onKeyDown(keyboardEvent: IKeyboardEvent): void {
let handled = false;
switch (keyboardEvent.keyCode) {
case KeyCode.Enter:
this._onNavigate.fire(keyboardEvent.shiftKey);
handled = true;
break;
case KeyCode.Escape:
this.clear();
handled = true;
break;
}
if (handled) {
keyboardEvent.preventDefault();
keyboardEvent.stopPropagation();
}
}
public dispose(): void {
if (this.options.focusKey) {
this.options.focusKey.set(false);
}
super.dispose();
}
}
export class FloatingClickWidget extends Widget implements IOverlayWidget {
private _domNode: HTMLElement;
private _onClick: Emitter<void> = this._register(new Emitter<void>());
public onClick: Event<void> = this._onClick.event;
constructor(
private editor: ICodeEditor,
private label: string,
private keyBindingAction: string,
@IKeybindingService keybindingService: IKeybindingService,
@IThemeService private themeService: IThemeService
) {
super();
if (keyBindingAction) {
let keybinding = keybindingService.lookupKeybinding(keyBindingAction);
if (keybinding) {
this.label += ' (' + keybinding.getLabel() + ')';
}
}
}
public render() {
this._domNode = DOM.$('.floating-click-widget');
this._register(attachStylerCallback(this.themeService, { buttonBackground, buttonForeground }, colors => {
this._domNode.style.backgroundColor = colors.buttonBackground;
this._domNode.style.color = colors.buttonForeground;
}));
DOM.append(this._domNode, DOM.$('')).textContent = this.label;
this.onclick(this._domNode, e => this._onClick.fire());
this.editor.addOverlayWidget(this);
}
public dispose(): void {
this.editor.removeOverlayWidget(this);
super.dispose();
}
public getId(): string {
return 'editor.overlayWidget.floatingClickWidget';
}
public getDomNode(): HTMLElement {
return this._domNode;
}
public getPosition(): IOverlayWidgetPosition {
return {
preference: OverlayWidgetPositionPreference.BOTTOM_RIGHT_CORNER
};
}
}
export class EditPreferenceWidget<T> extends Disposable {
public static GLYPH_MARGIN_CLASS_NAME = 'edit-preferences-widget';
private _line: number;
private _preferences: T[];
private _editPreferenceDecoration: string[];
private _onClick: Emitter<IEditorMouseEvent> = new Emitter<IEditorMouseEvent>();
public get onClick(): Event<IEditorMouseEvent> { return this._onClick.event; }
constructor(private editor: ICodeEditor
) {
super();
this._editPreferenceDecoration = [];
this._register(this.editor.onMouseDown((e: IEditorMouseEvent) => {
if (e.target.type !== MouseTargetType.GUTTER_GLYPH_MARGIN || /* after last line */ e.target.detail || !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: editorCommon.IModelDeltaDecoration[] = [];
this._line = line;
newDecoration.push({
options: {
glyphMarginClassName: EditPreferenceWidget.GLYPH_MARGIN_CLASS_NAME,
glyphMarginHoverMessage: new MarkdownString().appendText(hoverMessage),
stickiness: editorCommon.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();
}
}