Initial VS Code 1.19 source merge (#571)

* Initial 1.19 xcopy

* Fix yarn build

* Fix numerous build breaks

* Next batch of build break fixes

* More build break fixes

* Runtime breaks

* Additional post merge fixes

* Fix windows setup file

* Fix test failures.

* Update license header blocks to refer to source eula
This commit is contained in:
Karl Burtram
2018-01-28 23:37:17 -08:00
committed by GitHub
parent 9a1ac20710
commit 251ae01c3e
8009 changed files with 93378 additions and 35634 deletions

View File

@@ -82,11 +82,6 @@ class KeybindingInputWidget extends Widget {
this._chordPart = null;
}
public setAcceptChords(acceptChords: boolean) {
this._acceptChords = acceptChords;
this._chordPart = null;
}
private _onKeyDown(keyboardEvent: IKeyboardEvent): void {
keyboardEvent.preventDefault();
keyboardEvent.stopPropagation();
@@ -137,8 +132,8 @@ class KeybindingInputWidget extends Widget {
export class DefineKeybindingWidget extends Widget {
private static WIDTH = 400;
private static HEIGHT = 90;
private static readonly WIDTH = 400;
private static readonly HEIGHT = 90;
private _domNode: FastDomNode<HTMLElement>;
private _keybindingInputWidget: KeybindingInputWidget;
@@ -152,7 +147,6 @@ export class DefineKeybindingWidget extends Widget {
constructor(
parent: HTMLElement,
@IKeybindingService private keybindingService: IKeybindingService,
@IInstantiationService private instantiationService: IInstantiationService,
@IThemeService private themeService: IThemeService
) {
@@ -209,7 +203,7 @@ export class DefineKeybindingWidget extends Widget {
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.")));
dom.append(this._domNode.domNode, dom.$('.message', null, nls.localize('defineKeybinding.initial', "Press desired key combination and then press ENTER.")));
this._register(attachStylerCallback(this.themeService, { editorWidgetBackground, widgetShadow }, colors => {
this._domNode.domNode.style.backgroundColor = colors.editorWidgetBackground;
@@ -257,7 +251,7 @@ export class DefineKeybindingWidget extends Widget {
export class DefineKeybindingOverlayWidget extends Disposable implements IOverlayWidget {
private static ID = 'editor.contrib.defineKeybindingWidget';
private static readonly ID = 'editor.contrib.defineKeybindingWidget';
private readonly _widget: DefineKeybindingWidget;
@@ -295,4 +289,4 @@ export class DefineKeybindingOverlayWidget extends Disposable implements IOverla
this._widget.layout(new Dimension(layoutInfo.width, layoutInfo.height));
return this._widget.define();
}
}
}

View File

@@ -16,7 +16,7 @@ import { KeybindingLabel } from 'vs/base/browser/ui/keybindingLabel/keybindingLa
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 { EditorInput, EditorOptions } 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';
@@ -30,17 +30,17 @@ import {
} 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 { 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';
import { EditorExtensionsRegistry } from 'vs/editor/browser/editorExtensions';
import { WorkbenchList, IListService } from 'vs/platform/list/browser/listService';
let $ = DOM.$;
@@ -49,7 +49,7 @@ export class KeybindingsEditorInput extends EditorInput {
public static ID: string = 'workbench.input.keybindings';
public readonly keybindingsModel: KeybindingsEditorModel;
constructor( @IInstantiationService private instantiationService: IInstantiationService) {
constructor( @IInstantiationService instantiationService: IInstantiationService) {
super();
this.keybindingsModel = instantiationService.createInstance(KeybindingsEditorModel, OS);
}
@@ -106,7 +106,6 @@ export class KeybindingsEditor extends BaseEditor implements IKeybindingsEditor
@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,
@@ -132,23 +131,22 @@ export class KeybindingsEditor extends BaseEditor implements IKeybindingsEditor
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()));
this._register(focusTracker.onDidFocus(() => this.keybindingsEditorContextKey.set(true)));
this._register(focusTracker.onDidBlur(() => this.keybindingsEditorContextKey.reset()));
}
setInput(input: KeybindingsEditorInput): TPromise<void> {
setInput(input: KeybindingsEditorInput, options: EditorOptions): TPromise<void> {
const oldInput = this.input;
return super.setInput(input)
.then(() => {
if (!input.matches(oldInput)) {
this.render();
this.render(options && options.preserveFocus);
}
});
}
clearInput(): void {
super.clearInput();
this.searchWidget.clear();
this.keybindingsEditorContextKey.reset();
this.keybindingFocusContextKey.reset();
}
@@ -237,8 +235,8 @@ export class KeybindingsEditor extends BaseEditor implements IKeybindingsEditor
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() : ''
key: keybinding.keybindingItem.keybinding ? keybinding.keybindingItem.keybinding.getUserSettingsLabel() : '',
command: keybinding.keybindingItem.command
};
if (keybinding.keybindingItem.when) {
userFriendlyKeybinding.when = keybinding.keybindingItem.when;
@@ -328,28 +326,41 @@ export class KeybindingsEditor extends BaseEditor implements IKeybindingsEditor
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.keybindingsList = this._register(new WorkbenchList<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.contextKeyService, this.listService, this.themeService));
this._register(this.keybindingsList.onContextMenu(e => this.onContextMenu(e)));
this._register(this.keybindingsList.onFocusChange(e => this.onFocusChange(e)));
this._register(this.keybindingsList.onDOMFocus(() => {
this._register(this.keybindingsList.onDidFocus(() => {
DOM.addClass(this.keybindingsList.getHTMLElement(), 'focused');
}));
this._register(this.keybindingsList.onDOMBlur(() => {
this._register(this.keybindingsList.onDidBlur(() => {
DOM.removeClass(this.keybindingsList.getHTMLElement(), 'focused');
this.keybindingFocusContextKey.reset();
}));
this._register(attachListStyler(this.keybindingsList, this.themeService));
this._register(this.listService.register(this.keybindingsList));
this._register(this.keybindingsList.onKeyUp(e => {
const event = new StandardKeyboardEvent(e);
if (event.keyCode === KeyCode.Enter) {
const keybindingEntry = this.activeKeybindingEntry;
if (keybindingEntry) {
this.defineKeybinding(this.activeKeybindingEntry);
}
e.stopPropagation();
}
}));
}
private render(): TPromise<any> {
private render(preserveFocus?: boolean): TPromise<any> {
if (this.input) {
return this.input.resolve()
.then((keybindingsModel: KeybindingsEditorModel) => this.keybindingsEditorModel = keybindingsModel)
.then(() => this.keybindingsEditorModel.resolve())
.then(() => this.renderKeybindingsEntries(false));
.then(() => {
const editorActionsLabels: { [id: string]: string; } = EditorExtensionsRegistry.getEditorActions().reduce((editorActions, editorAction) => {
editorActions[editorAction.id] = editorAction.label;
return editorActions;
}, {});
return this.keybindingsEditorModel.resolve(editorActionsLabels);
})
.then(() => this.renderKeybindingsEntries(false, preserveFocus));
}
return TPromise.as(null);
}
@@ -359,7 +370,7 @@ export class KeybindingsEditor extends BaseEditor implements IKeybindingsEditor
this.delayedFilterLogging.trigger(() => this.reportFilteringUsed(this.searchWidget.getValue()));
}
private renderKeybindingsEntries(reset: boolean): void {
private renderKeybindingsEntries(reset: boolean, preserveFocus?: boolean): void {
if (this.keybindingsEditorModel) {
const filter = this.searchWidget.getValue();
const keybindingsEntries: IKeybindingItemEntry[] = this.keybindingsEditorModel.fetch(filter, this.sortByPrecedence.checked);
@@ -384,7 +395,7 @@ export class KeybindingsEditor extends BaseEditor implements IKeybindingsEditor
this.unAssignedKeybindingItemToRevealAndFocus = null;
} else if (currentSelectedIndex !== -1 && currentSelectedIndex < this.listEntries.length) {
this.selectEntry(currentSelectedIndex);
} else if (this.editorService.getActiveEditor() === this) {
} else if (this.editorService.getActiveEditor() === this && !preserveFocus) {
this.focus();
}
}
@@ -737,7 +748,7 @@ class CommandColumn extends Column {
commandLabel.set(keybindingItem.command, keybindingItemEntry.commandIdMatches);
}
if (commandLabel) {
commandLabel.element.title = keybindingItem.command;
commandLabel.element.title = keybindingItem.commandLabel ? localize('title', "{0} ({1})", keybindingItem.commandLabel, keybindingItem.command) : keybindingItem.command;
}
}
@@ -821,4 +832,4 @@ registerThemingParticipant((theme: ITheme, collector: ICssStyleCollector) => {
if (listHighlightForegroundColor) {
collector.addRule(`.keybindings-editor > .keybindings-body > .keybindings-list-container .monaco-list-row > .column .highlight { color: ${listHighlightForegroundColor}; }`);
}
});
});

View File

@@ -15,10 +15,9 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti
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 { registerEditorContribution, ServicesAccessor, registerEditorCommand, EditorCommand } from 'vs/editor/browser/editorExtensions';
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
import { editorContribution } from 'vs/editor/browser/editorBrowserExtensions';
import { SnippetController2 } from 'vs/editor/contrib/snippet/browser/snippetController2';
import { SnippetController2 } from 'vs/editor/contrib/snippet/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';
@@ -35,12 +34,11 @@ const NLS_KB_LAYOUT_ERROR_MESSAGE = nls.localize('defineKeybinding.kbLayoutError
const INTERESTING_FILE = /keybindings\.json$/;
@editorContribution
export class DefineKeybindingController extends Disposable implements editorCommon.IEditorContribution {
private static ID = 'editor.contrib.defineKeybinding';
private static readonly ID = 'editor.contrib.defineKeybinding';
public static get(editor: editorCommon.ICommonCodeEditor): DefineKeybindingController {
public static get(editor: ICodeEditor): DefineKeybindingController {
return editor.getContribution<DefineKeybindingController>(DefineKeybindingController.ID);
}
@@ -371,7 +369,7 @@ class DefineKeybindingCommand extends EditorCommand {
});
}
public runEditorCommand(accessor: ServicesAccessor, editor: editorCommon.ICommonCodeEditor): void {
public runEditorCommand(accessor: ServicesAccessor, editor: ICodeEditor): void {
if (!isInterestingEditorModel(editor) || editor.getConfiguration().readOnly) {
return;
}
@@ -382,7 +380,7 @@ class DefineKeybindingCommand extends EditorCommand {
}
}
function isInterestingEditorModel(editor: editorCommon.ICommonCodeEditor): boolean {
function isInterestingEditorModel(editor: ICodeEditor): boolean {
let model = editor.getModel();
if (!model) {
return false;
@@ -391,4 +389,5 @@ function isInterestingEditorModel(editor: editorCommon.ICommonCodeEditor): boole
return INTERESTING_FILE.test(url);
}
registerEditorContribution(DefineKeybindingController);
registerEditorCommand(new DefineKeybindingCommand());

View File

@@ -22,42 +22,79 @@
position: relative;
}
.settings-targets-widget {
flex-wrap: wrap;
margin: 4px 0 4px 18px;
display: flex;
border-radius: 4px;
padding: 0 8px;
cursor: pointer;
.preferences-editor > .preferences-editors-container.side-by-side-preferences-editor .preferences-header-container {
line-height: 28px;
}
.settings-targets-widget > .settings-target {
font-size: 11px;
padding: 2px 4px 0 0;
white-space: nowrap;
.settings-tabs-widget > .monaco-action-bar .action-item.disabled {
display: none;
}
.settings-tabs-widget > .monaco-action-bar .action-item {
max-width: 300px;
overflow: hidden;
flex: 1;
display: flex;
text-overflow: ellipsis;
}
.settings-targets-widget > .settings-target > .settings-target-label {
.default-preferences-editor-container > .preferences-header-container > .default-preferences-header,
.settings-tabs-widget > .monaco-action-bar .action-item .action-label {
text-transform: uppercase;
font-size: 11px;
margin-left: 33px;
margin-right: 5px;
cursor: pointer;
display: flex;
overflow: hidden;
text-overflow: ellipsis;
}
.settings-targets-widget > .settings-target > .settings-target-details {
.settings-tabs-widget > .monaco-action-bar .actions-container {
justify-content: flex-start;
}
.settings-tabs-widget > .monaco-action-bar .action-item {
padding: 3px 0px;
}
.settings-tabs-widget > .monaco-action-bar .action-item .action-title {
text-overflow: ellipsis;
overflow: hidden;
}
.settings-tabs-widget > .monaco-action-bar .action-item .action-details {
text-transform: none;
margin-left: 0.5em;
font-size: 10px;
opacity: 0.7;
}
.settings-targets-widget > .settings-target > .settings-target-details.empty {
margin-left: 0;
.settings-tabs-widget .monaco-action-bar .action-item .dropdown-icon {
padding-left: 0.3em;
padding-top: 8px;
font-size: 12px;
}
.settings-targets-widget > .settings-target-dropdown-icon {
padding-left: 0.5em;
padding-top: 4px;
font-size: 12px;
.settings-tabs-widget .monaco-action-bar .action-item .dropdown-icon.hide {
display: none;
}
.vs .settings-tabs-widget > .monaco-action-bar .action-item .action-label {
color: #424242;
opacity: 0.75;
}
.vs-dark .settings-tabs-widget > .monaco-action-bar .action-item .action-label {
color: #e7e7e7;
opacity: 0.5;
}
.hc-black .settings-tabs-widget > .monaco-action-bar .action-item .action-label {
color: #fff;
}
.settings-tabs-widget > .monaco-action-bar .action-item:hover .action-label,
.settings-tabs-widget > .monaco-action-bar .action-item .action-label.checked {
opacity: 1;
}
.preferences-header > .settings-header-widget {
@@ -89,20 +126,19 @@
display: none;
}
.settings-header-widget > .settings-search-controls > .prefs-fuzzy-search-toggle {
.settings-header-widget > .settings-search-controls > .prefs-natural-language-search-toggle {
margin: 5px 3px 5px 0px;
}
.settings-header-widget > .settings-search-controls > .prefs-fuzzy-search-toggle.hidden {
.settings-header-widget > .settings-search-controls > .prefs-natural-language-search-toggle.hidden {
display: none;
}
.vs .settings-header-widget > .settings-search-controls > .prefs-fuzzy-search-toggle {
background: url('regex.svg') center center no-repeat;
}
.vs-dark .settings-header-widget > .settings-search-controls > .prefs-fuzzy-search-toggle {
background: url('regex-dark.svg') center center no-repeat;
.settings-header-widget > .settings-search-controls > .prefs-natural-language-search-toggle > .octicon {
text-align: center;
vertical-align: top;
font-size: 16px;
width: 100%;
}
.settings-header-widget > .settings-search-container {
@@ -155,13 +191,13 @@
white-space: nowrap;
}
.monaco-editor .settings-header-widget .title-container .settings-header-fuzzy-link {
.monaco-editor .settings-header-widget .title-container .settings-header-natural-language-link {
margin-left: 4px;
text-decoration: underline;
cursor: pointer;
}
.monaco-editor .settings-header-widget .title-container .settings-header-fuzzy-link.hidden {
.monaco-editor .settings-header-widget .title-container .settings-header-natural-language-link.hidden {
display: none;
}

View File

@@ -1,265 +0,0 @@
/*---------------------------------------------------------------------------------------------
* 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/actions';
import { EditorInput, IEditorInputFactory, IEditorInputFactoryRegistry, Extensions as EditorInputExtensions } from 'vs/workbench/common/editor';
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, OPEN_FOLDER_SETTINGS_COMMAND } 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, KEYBINDINGS_EDITOR_COMMAND_CLEAR_SEARCH_RESULTS
} from 'vs/workbench/parts/preferences/common/preferences';
import { PreferencesService } from 'vs/workbench/parts/preferences/browser/preferencesService';
import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } from 'vs/workbench/common/contributions';
import { PreferencesContribution } from 'vs/workbench/parts/preferences/common/preferencesContribution';
import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService';
import { CommandsRegistry } from 'vs/platform/commands/common/commands';
import { IWorkspaceFolder } from 'vs/platform/workspace/common/workspace';
import { IEditorRegistry, EditorDescriptor, Extensions as EditorExtensions } from 'vs/workbench/browser/editor';
registerSingleton(IPreferencesService, PreferencesService);
Registry.as<IEditorRegistry>(EditorExtensions.Editors).registerEditor(
new EditorDescriptor(
PreferencesEditor,
PreferencesEditor.ID,
nls.localize('defaultPreferencesEditor', "Default Preferences Editor")
),
[
new SyncDescriptor(PreferencesEditorInput)
]
);
Registry.as<IEditorRegistry>(EditorExtensions.Editors).registerEditor(
new EditorDescriptor(
KeybindingsEditor,
KeybindingsEditor.ID,
nls.localize('keybindingsEditor', "Keybindings Editor")
),
[
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<IEditorInputFactoryRegistry>(EditorInputExtensions.EditorInputFactories);
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<IEditorInputFactoryRegistry>(EditorInputExtensions.EditorInputFactories);
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<IEditorInputFactoryRegistry>(EditorInputExtensions.EditorInputFactories).registerEditorInputFactory(PreferencesEditorInput.ID, PreferencesEditorInputFactory);
Registry.as<IEditorInputFactoryRegistry>(EditorInputExtensions.EditorInputFactories).registerEditorInputFactory(DefaultPreferencesEditorInput.ID, DefaultPreferencesEditorInputFactory);
Registry.as<IEditorInputFactoryRegistry>(EditorInputExtensions.EditorInputFactories).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();
}
});
KeybindingsRegistry.registerCommandAndKeybindingRule({
id: KEYBINDINGS_EDITOR_COMMAND_CLEAR_SEARCH_RESULTS,
weight: KeybindingsRegistry.WEIGHT.workbenchContrib(),
when: ContextKeyExpr.and(CONTEXT_KEYBINDINGS_EDITOR, CONTEXT_KEYBINDINGS_SEARCH_FOCUS),
primary: KeyCode.Escape,
handler: (accessor, args: any) => {
const editor = accessor.get(IWorkbenchEditorService).getActiveEditor() as IKeybindingsEditor;
editor.clearSearchResults();
}
});
Registry.as<IWorkbenchContributionsRegistry>(WorkbenchExtensions.Workbench).registerWorkbenchContribution(PreferencesContribution);
CommandsRegistry.registerCommand(OPEN_FOLDER_SETTINGS_COMMAND, function (accessor: ServicesAccessor, args?: IWorkspaceFolder) {
const preferencesService = accessor.get(IPreferencesService);
return preferencesService.openFolderSettings(args.uri);
});

View File

@@ -16,10 +16,28 @@ import { IWorkspaceContextService, WorkbenchState, IWorkspaceFolder } from 'vs/p
import { ICommandService } from 'vs/platform/commands/common/commands';
import { PICK_WORKSPACE_FOLDER_COMMAND } from 'vs/workbench/browser/actions/workspaceActions';
export class OpenRawDefaultSettingsAction extends Action {
public static readonly ID = 'workbench.action.openRawDefaultSettings';
public static readonly LABEL = nls.localize('openRawDefaultSettings', "Open Raw Default Settings");
constructor(
id: string,
label: string,
@IPreferencesService private preferencesService: IPreferencesService
) {
super(id, label);
}
public run(event?: any): TPromise<any> {
return this.preferencesService.openRawDefaultSettings();
}
}
export class OpenGlobalSettingsAction extends Action {
public static ID = 'workbench.action.openGlobalSettings';
public static LABEL = nls.localize('openGlobalSettings', "Open User Settings");
public static readonly ID = 'workbench.action.openGlobalSettings';
public static readonly LABEL = nls.localize('openGlobalSettings', "Open User Settings");
constructor(
id: string,
@@ -36,8 +54,8 @@ export class OpenGlobalSettingsAction extends Action {
export class OpenGlobalKeybindingsAction extends Action {
public static ID = 'workbench.action.openGlobalKeybindings';
public static LABEL = nls.localize('openGlobalKeybindings', "Open Keyboard Shortcuts");
public static readonly ID = 'workbench.action.openGlobalKeybindings';
public static readonly LABEL = nls.localize('openGlobalKeybindings', "Open Keyboard Shortcuts");
constructor(
id: string,
@@ -54,8 +72,8 @@ export class OpenGlobalKeybindingsAction extends Action {
export class OpenGlobalKeybindingsFileAction extends Action {
public static ID = 'workbench.action.openGlobalKeybindingsFile';
public static LABEL = nls.localize('openGlobalKeybindingsFile', "Open Keyboard Shortcuts File");
public static readonly ID = 'workbench.action.openGlobalKeybindingsFile';
public static readonly LABEL = nls.localize('openGlobalKeybindingsFile', "Open Keyboard Shortcuts File");
constructor(
id: string,
@@ -72,8 +90,8 @@ export class OpenGlobalKeybindingsFileAction extends Action {
export class OpenWorkspaceSettingsAction extends Action {
public static ID = 'workbench.action.openWorkspaceSettings';
public static LABEL = nls.localize('openWorkspaceSettings', "Open Workspace Settings");
public static readonly ID = 'workbench.action.openWorkspaceSettings';
public static readonly LABEL = nls.localize('openWorkspaceSettings', "Open Workspace Settings");
private disposables: IDisposable[] = [];
@@ -105,8 +123,8 @@ export class OpenWorkspaceSettingsAction extends Action {
export const OPEN_FOLDER_SETTINGS_COMMAND = '_workbench.action.openFolderSettings';
export class OpenFolderSettingsAction extends Action {
public static ID = 'workbench.action.openFolderSettings';
public static LABEL = nls.localize('openFolderSettings', "Open Folder Settings");
public static readonly ID = 'workbench.action.openFolderSettings';
public static readonly LABEL = nls.localize('openFolderSettings', "Open Folder Settings");
private disposables: IDisposable[] = [];
@@ -114,7 +132,6 @@ export class OpenFolderSettingsAction extends Action {
constructor(
id: string,
label: string,
@IPreferencesService private preferencesService: IPreferencesService,
@IWorkspaceContextService private workspaceContextService: IWorkspaceContextService,
@ICommandService private commandService: ICommandService
) {
@@ -146,8 +163,8 @@ export class OpenFolderSettingsAction extends Action {
export class ConfigureLanguageBasedSettingsAction extends Action {
public static ID = 'workbench.action.configureLanguageBasedSettings';
public static LABEL = nls.localize('configureLanguageBasedSettings', "Configure Language Specific Settings...");
public static readonly ID = 'workbench.action.configureLanguageBasedSettings';
public static readonly LABEL = nls.localize('configureLanguageBasedSettings', "Configure Language Specific Settings...");
constructor(
id: string,

View File

@@ -14,6 +14,7 @@ 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 { SideBySideEditorInput, EditorOptions, EditorInput } from 'vs/workbench/common/editor';
import { Scope } from 'vs/workbench/common/memento';
import { BaseEditor } 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';
@@ -23,35 +24,29 @@ 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, SETTINGS_EDITOR_COMMAND_CLEAR_SEARCH_RESULTS, SETTINGS_EDITOR_COMMAND_FOCUS_NEXT_SETTING, SETTINGS_EDITOR_COMMAND_FOCUS_PREVIOUS_SETTING
IPreferencesService, ISettingsGroup, ISetting, IFilterResult, IPreferencesSearchService,
CONTEXT_SETTINGS_EDITOR, CONTEXT_SETTINGS_SEARCH_FOCUS, SETTINGS_EDITOR_COMMAND_SEARCH, SETTINGS_EDITOR_COMMAND_FOCUS_FILE, ISettingsEditorModel, SETTINGS_EDITOR_COMMAND_CLEAR_SEARCH_RESULTS, SETTINGS_EDITOR_COMMAND_FOCUS_NEXT_SETTING, SETTINGS_EDITOR_COMMAND_FOCUS_PREVIOUS_SETTING, IFilterMetadata, IPreferencesSearchModel
} 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 { PreferencesSearchProvider, PreferencesSearchModel } from 'vs/workbench/parts/preferences/browser/preferencesSearch';
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
import { SearchWidget, SettingsTargetsWidget, SettingsTarget } 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 { registerEditorContribution, Command, IEditorContributionCtor } from 'vs/editor/browser/editorExtensions';
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 { 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';
import { IEditorRegistry, Extensions as EditorExtensions } from 'vs/workbench/browser/editor';
import { FoldingController } from 'vs/editor/contrib/folding/browser/folding';
import { FindController } from 'vs/editor/contrib/find/browser/find';
import { SelectionHighlighter } from 'vs/editor/contrib/multicursor/common/multicursor';
import { FoldingController } from 'vs/editor/contrib/folding/folding';
import { FindController } from 'vs/editor/contrib/find/findController';
import { SelectionHighlighter } from 'vs/editor/contrib/multicursor/multicursor';
import { IEditorOptions } from 'vs/editor/common/config/editorOptions';
import { KeybindingsRegistry } from 'vs/platform/keybinding/common/keybindingsRegistry';
import { attachStylerCallback } from 'vs/platform/theme/common/styler';
@@ -62,6 +57,7 @@ import { Registry } from 'vs/platform/registry/common/platform';
import { MessageController } from 'vs/editor/contrib/message/messageController';
import { ConfigurationTarget } from 'vs/platform/configuration/common/configuration';
import { IHashService } from 'vs/workbench/services/hash/common/hashService';
import { ConfigurationScope } from 'vs/platform/configuration/common/configurationRegistry';
export class PreferencesEditorInput extends SideBySideEditorInput {
public static ID: string = 'workbench.editorinputs.preferencesEditorInput';
@@ -70,13 +66,17 @@ export class PreferencesEditorInput extends SideBySideEditorInput {
return PreferencesEditorInput.ID;
}
public supportsSplitEditor(): boolean {
return true;
}
public getTitle(verbosity: Verbosity): string {
return this.master.getTitle(verbosity);
}
}
export class DefaultPreferencesEditorInput extends ResourceEditorInput {
public static ID = 'workbench.editorinputs.defaultpreferences';
public static readonly ID = 'workbench.editorinputs.defaultpreferences';
constructor(defaultSettingsResource: URI,
@ITextModelService textModelResolverService: ITextModelService,
@IHashService hashService: IHashService
@@ -107,33 +107,32 @@ export class PreferencesEditor extends BaseEditor {
private focusSettingsContextKey: IContextKey<boolean>;
private headerContainer: HTMLElement;
private searchWidget: SearchWidget;
private settingsTargetsWidget: SettingsTargetsWidget;
private sideBySidePreferencesWidget: SideBySidePreferencesWidget;
private preferencesRenderers: PreferencesRenderers;
private searchProvider: PreferencesSearchProvider;
private delayedFilterLogging: Delayer<void>;
private filterThrottle: ThrottledDelayer<void>;
private latestEmptyFilters: string[] = [];
private lastFocusedWidget: SearchWidget | SideBySidePreferencesWidget = null;
private memento: any;
constructor(
@IPreferencesService private preferencesService: IPreferencesService,
@IEnvironmentService private environmentService: IEnvironmentService,
@IPreferencesSearchService private preferencesSearchService: IPreferencesSearchService,
@ITelemetryService telemetryService: ITelemetryService,
@IWorkbenchEditorService private editorService: IWorkbenchEditorService,
@IContextKeyService private contextKeyService: IContextKeyService,
@IInstantiationService private instantiationService: IInstantiationService,
@IThemeService themeService: IThemeService,
@IWorkspaceContextService private workspaceContextService: IWorkspaceContextService,
@IStorageService storageService: IStorageService,
) {
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);
this.searchProvider = this.instantiationService.createInstance(PreferencesSearchProvider);
this.filterThrottle = new ThrottledDelayer(200);
this.memento = this.getMemento(storageService, Scope.WORKSPACE);
}
public createEditor(parent: Builder): void {
@@ -145,29 +144,30 @@ export class PreferencesEditor extends BaseEditor {
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
focusKey: this.focusSettingsContextKey,
showFuzzyToggle: true,
showResultCount: true
}));
this.searchWidget.setFuzzyToggleVisible(this.searchProvider.remoteSearchEnabled);
this._register(this.searchProvider.onRemoteSearchEnablementChanged(enabled => this.searchWidget.setFuzzyToggleVisible(enabled)));
this.searchWidget.setFuzzyToggleVisible(this.preferencesSearchService.remoteSearchAllowed);
this.searchWidget.fuzzyEnabled = this.memento['fuzzyEnabled'];
this._register(this.preferencesSearchService.onRemoteSearchEnablementChanged(enabled => this.searchWidget.setFuzzyToggleVisible(enabled)));
this._register(this.searchWidget.onDidChange(value => this.onInputChanged()));
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._register(this.sideBySidePreferencesWidget.onDidSettingsTargetChange(target => this.switchSettings(target)));
this.preferencesRenderers = this._register(new PreferencesRenderers());
this._register(this.workspaceContextService.onDidChangeWorkspaceFolders(() => this.onWorkspaceFoldersChanged()));
this._register(this.workspaceContextService.onDidChangeWorkbenchState(() => this.onWorkbenchStateChanged()));
this.preferencesRenderers = this._register(new PreferencesRenderers(this.preferencesSearchService));
this._register(this.preferencesRenderers.onTriggeredFuzzy(() => {
this.searchWidget.fuzzyEnabled = true;
this.filterPreferences();
}));
this._register(this.preferencesRenderers.onDidFilterResultsCountChange(count => this.showSearchResultsMessage(count)));
}
public clearSearchResults(): void {
@@ -242,9 +242,6 @@ export class PreferencesEditor extends BaseEditor {
}
private updateInput(oldInput: PreferencesEditorInput, newInput: PreferencesEditorInput, options?: EditorOptions): TPromise<void> {
const resource = newInput.master.getResource();
this.settingsTargetsWidget.updateTargets(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;
@@ -264,98 +261,68 @@ export class PreferencesEditor extends BaseEditor {
this.filterThrottle.trigger(() => this.filterPreferences());
}
private getSettingsConfigurationTarget(resource: URI): ConfigurationTarget {
if (this.preferencesService.userSettingsResource.toString() === resource.toString()) {
return ConfigurationTarget.USER;
}
const workspaceSettingsResource = this.preferencesService.workspaceSettingsResource;
if (workspaceSettingsResource && workspaceSettingsResource.toString() === resource.toString()) {
return ConfigurationTarget.WORKSPACE;
}
if (this.workspaceContextService.getWorkspaceFolder(resource)) {
return ConfigurationTarget.WORKSPACE_FOLDER;
}
return null;
}
private getSettingsConfigurationTargetUri(resource: URI): URI {
if (this.preferencesService.userSettingsResource.toString() === resource.toString()) {
return resource;
}
if (this.preferencesService.workspaceSettingsResource.toString() === resource.toString()) {
return resource;
}
const workspaceFolder = this.workspaceContextService.getWorkspaceFolder(resource);
return workspaceFolder ? workspaceFolder.uri : null;
}
private onWorkspaceFoldersChanged(): void {
if (this.input) {
const settingsResource = (<PreferencesEditorInput>this.input).master.getResource();
const targetResource = this.getSettingsConfigurationTargetUri(settingsResource);
if (!targetResource) {
this.switchSettings(this.preferencesService.userSettingsResource);
}
}
}
private onWorkbenchStateChanged(): void {
if (this.input) {
const editableSettingsResource = (<PreferencesEditorInput>this.input).master.getResource();
const newConfigurationTarget = this.getSettingsConfigurationTarget(editableSettingsResource);
if (newConfigurationTarget) {
if (newConfigurationTarget !== this.settingsTargetsWidget.configurationTarget) {
// Update the editor if the configuration target of the settings resource changed
this.switchSettings(editableSettingsResource);
}
} else {
this.switchSettings(this.preferencesService.userSettingsResource);
}
}
}
private switchSettings(resource: URI): void {
private switchSettings(target: SettingsTarget): 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));
promise.done(value => {
if (target === ConfigurationTarget.USER) {
this.preferencesService.switchSettings(ConfigurationTarget.USER, this.preferencesService.userSettingsResource);
} else if (target === ConfigurationTarget.WORKSPACE) {
this.preferencesService.switchSettings(ConfigurationTarget.WORKSPACE, this.preferencesService.workspaceSettingsResource);
} else if (target instanceof URI) {
this.preferencesService.switchSettings(ConfigurationTarget.WORKSPACE_FOLDER, target);
}
});
}
private filterPreferences(): TPromise<void> {
this.memento['fuzzyEnabled'] = this.searchWidget.fuzzyEnabled;
const filter = this.searchWidget.getValue().trim();
return this.preferencesRenderers.filterPreferences(filter, this.searchProvider, this.searchWidget.fuzzyEnabled).then(count => {
const message = filter ? this.showSearchResultsMessage(count) : nls.localize('totalSettingsMessage', "Total {0} Settings", count);
this.searchWidget.showMessage(message, count);
if (count === 0) {
return this.preferencesRenderers.filterPreferences({ filter, fuzzy: this.searchWidget.fuzzyEnabled }).then(result => {
this.showSearchResultsMessage(result.count);
if (result.count === 0) {
this.latestEmptyFilters.push(filter);
}
this.delayedFilterLogging.trigger(() => this.reportFilteringUsed(filter));
this.preferencesRenderers.focusFirst();
this.delayedFilterLogging.trigger(() => this.reportFilteringUsed(filter, result.metadata));
}, onUnexpectedError);
}
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 showSearchResultsMessage(count: number): void {
if (this.searchWidget.getValue()) {
if (count === 0) {
this.searchWidget.showMessage(nls.localize('noSettingsFound', "No Results"), count);
} else if (count === 1) {
this.searchWidget.showMessage(nls.localize('oneSettingFound', "1 Setting matched"), count);
} else {
this.searchWidget.showMessage(nls.localize('settingsFound', "{0} Settings matched", count), count);
}
} else {
this.searchWidget.showMessage(nls.localize('totalSettingsMessage', "Total {0} Settings", count), count);
}
}
private reportFilteringUsed(filter: string): void {
private reportFilteringUsed(filter: string, metadata?: IFilterMetadata): void {
if (filter) {
let data = {
filter,
emptyFilters: this.getLatestEmptyFiltersForTelemetry()
emptyFilters: this.getLatestEmptyFiltersForTelemetry(),
fuzzy: !!metadata,
duration: metadata ? metadata.duration : undefined,
context: metadata ? metadata.context : undefined
};
this.latestEmptyFilters = [];
/* __GDPR__
"defaultSettings.filter" : {
"filter": { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
"emptyFilters" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
"emptyFilters" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
"fuzzy" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
"duration" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
"context" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
}
*/
this.telemetryService.publicLog('defaultSettings.filter', data);
@@ -405,32 +372,57 @@ class SettingsNavigator implements INavigator<ISetting> {
}
}
interface ISearchCriteria {
filter: string;
fuzzy: boolean;
}
class PreferencesRenderers extends Disposable {
private _defaultPreferencesRenderer: IPreferencesRenderer<ISetting>;
private _defaultPreferencesRendererDisposables: IDisposable[] = [];
private _defaultPreferencesFilterResult: IFilterResult;
private _editablePreferencesFilterResult: IFilterResult;
private _editablePreferencesRenderer: IPreferencesRenderer<ISetting>;
private _editablePreferencesRendererDisposables: IDisposable[] = [];
private _settingsNavigator: SettingsNavigator;
private _filtersInProgress: TPromise<any>[];
private _searchCriteria: ISearchCriteria;
private _currentSearchModel: IPreferencesSearchModel;
private _disposables: IDisposable[] = [];
private _onTriggeredFuzzy: Emitter<void> = new Emitter<void>();
private _onTriggeredFuzzy: Emitter<void> = this._register(new Emitter<void>());
public onTriggeredFuzzy: Event<void> = this._onTriggeredFuzzy.event;
public get defaultPreferencesRenderer(): IPreferencesRenderer<ISetting> {
private _onDidFilterResultsCountChange: Emitter<number> = this._register(new Emitter<number>());
public onDidFilterResultsCountChange: Event<number> = this._onDidFilterResultsCountChange.event;
constructor(
private preferencesSearchService: IPreferencesSearchService
) {
super();
}
get defaultPreferencesRenderer(): IPreferencesRenderer<ISetting> {
return this._defaultPreferencesRenderer;
}
public set defaultPreferencesRenderer(defaultPreferencesRenderer: IPreferencesRenderer<ISetting>) {
get editablePreferencesRenderer(): IPreferencesRenderer<ISetting> {
return this._editablePreferencesRenderer;
}
set defaultPreferencesRenderer(defaultPreferencesRenderer: IPreferencesRenderer<ISetting>) {
if (this._defaultPreferencesRenderer !== defaultPreferencesRenderer) {
this._defaultPreferencesRenderer = defaultPreferencesRenderer;
this._disposables = dispose(this._disposables);
this._defaultPreferencesRendererDisposables = dispose(this._defaultPreferencesRendererDisposables);
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);
this._defaultPreferencesRenderer.onUpdatePreference(({ key, value, source, index }) => this._updatePreference(key, value, source, index, this._editablePreferencesRenderer), this, this._defaultPreferencesRendererDisposables);
this._defaultPreferencesRenderer.onFocusPreference(preference => this._focusPreference(preference, this._editablePreferencesRenderer), this, this._defaultPreferencesRendererDisposables);
this._defaultPreferencesRenderer.onClearFocusPreference(preference => this._clearFocus(preference, this._editablePreferencesRenderer), this, this._defaultPreferencesRendererDisposables);
if (this._defaultPreferencesRenderer.onTriggeredFuzzy) {
this._register(this._defaultPreferencesRenderer.onTriggeredFuzzy(() => this._onTriggeredFuzzy.fire()));
}
@@ -438,37 +430,50 @@ class PreferencesRenderers extends Disposable {
}
}
public set editablePreferencesRenderer(editableSettingsRenderer: IPreferencesRenderer<ISetting>) {
this._editablePreferencesRenderer = editableSettingsRenderer;
set editablePreferencesRenderer(editableSettingsRenderer: IPreferencesRenderer<ISetting>) {
if (this._editablePreferencesRenderer !== editableSettingsRenderer) {
this._editablePreferencesRenderer = editableSettingsRenderer;
this._editablePreferencesRendererDisposables = dispose(this._editablePreferencesRendererDisposables);
if (this._editablePreferencesRenderer) {
(<ISettingsEditorModel>this._editablePreferencesRenderer.preferencesModel).onDidChangeGroups(() => {
if (this._currentSearchModel) {
this._filterEditablePreferences()
.then(() => {
const count = this.consolidateAndUpdate();
this._onDidFilterResultsCountChange.fire(count);
});
}
}, this, this._editablePreferencesRendererDisposables);
}
}
}
public filterPreferences(filter: string, searchProvider: PreferencesSearchProvider, fuzzy: boolean): TPromise<number> {
filterPreferences(criteria: ISearchCriteria): TPromise<{ count: number, metadata: IFilterMetadata }> {
this._searchCriteria = criteria;
if (this._filtersInProgress) {
// Resolved/rejected promises have no .cancel()
this._filtersInProgress.forEach(p => p.cancel && p.cancel());
}
const searchModel = searchProvider.startSearch(filter, fuzzy);
this._filtersInProgress = [
this._filterPreferences(searchModel, searchProvider, this._defaultPreferencesRenderer),
this._filterPreferences(searchModel, searchProvider, this._editablePreferencesRenderer)];
this._currentSearchModel = this.preferencesSearchService.startSearch(this._searchCriteria.filter, criteria.fuzzy);
this._filtersInProgress = [this._filterDefaultPreferences(), this._filterEditablePreferences()];
return TPromise.join<IFilterResult>(this._filtersInProgress).then(filterResults => {
this._filtersInProgress = null;
const defaultPreferencesFilterResult = filterResults[0];
const editablePreferencesFilterResult = filterResults[1];
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;
return TPromise.join<IFilterResult>(this._filtersInProgress).then(() => {
const count = this.consolidateAndUpdate();
return { count, metadata: this._defaultPreferencesFilterResult && this._defaultPreferencesFilterResult.metadata };
});
}
public focusNextPreference(forward: boolean = true) {
focusFirst(): void {
// Focus first match in both renderers
this._focusPreference(this._getFirstSettingFromTheGroups(this._defaultPreferencesFilterResult ? this._defaultPreferencesFilterResult.filteredGroups : []), this._defaultPreferencesRenderer);
this._focusPreference(this._getFirstSettingFromTheGroups(this._editablePreferencesFilterResult ? this._editablePreferencesFilterResult.filteredGroups : []), this._editablePreferencesRenderer);
this._settingsNavigator.first(); // Move to first
}
focusNextPreference(forward: boolean = true) {
if (!this._settingsNavigator) {
return;
}
@@ -478,21 +483,54 @@ class PreferencesRenderers extends Disposable {
this._focusPreference(setting, this._editablePreferencesRenderer);
}
private _filterDefaultPreferences(): TPromise<void> {
if (this._searchCriteria && this._defaultPreferencesRenderer) {
return this._filterPreferences(this._searchCriteria, this._defaultPreferencesRenderer, this._currentSearchModel)
.then(filterResult => { this._defaultPreferencesFilterResult = filterResult; });
}
return TPromise.wrap(null);
}
private _filterEditablePreferences(): TPromise<void> {
if (this._searchCriteria && this._editablePreferencesRenderer) {
return this._filterPreferences(this._searchCriteria, this._editablePreferencesRenderer, this._currentSearchModel)
.then(filterResult => { this._editablePreferencesFilterResult = filterResult; });
}
return TPromise.wrap(null);
}
private _getFirstSettingFromTheGroups(allGroups: ISettingsGroup[]): ISetting {
if (allGroups.length) {
if (allGroups[0].sections.length) {
return allGroups[0].sections[0].settings[0];
}
}
return null;
}
private _getAllPreferences(preferencesRenderer: IPreferencesRenderer<ISetting>): ISettingsGroup[] {
return preferencesRenderer ? (<ISettingsEditorModel>preferencesRenderer.preferencesModel).settingsGroups : [];
}
private _filterPreferences(searchModel: PreferencesSearchModel, searchProvider: PreferencesSearchProvider, preferencesRenderer: IPreferencesRenderer<ISetting>): TPromise<IFilterResult> {
if (preferencesRenderer) {
private _filterPreferences(searchCriteria: ISearchCriteria, preferencesRenderer: IPreferencesRenderer<ISetting>, searchModel: IPreferencesSearchModel): TPromise<IFilterResult> {
if (preferencesRenderer && searchCriteria) {
const prefSearchP = searchModel.filterPreferences(<ISettingsEditorModel>preferencesRenderer.preferencesModel);
return prefSearchP.then(filterResult => {
preferencesRenderer.filterPreferences(filterResult, searchProvider.remoteSearchEnabled);
preferencesRenderer.filterPreferences(filterResult, this.preferencesSearchService.remoteSearchAllowed);
return filterResult;
});
}
return TPromise.as(null);
}
return TPromise.wrap(null);
private consolidateAndUpdate(): number {
const defaultPreferencesFilteredGroups = this._defaultPreferencesFilterResult ? this._defaultPreferencesFilterResult.filteredGroups : this._getAllPreferences(this._defaultPreferencesRenderer);
const editablePreferencesFilteredGroups = this._editablePreferencesFilterResult ? this._editablePreferencesFilterResult.filteredGroups : this._getAllPreferences(this._editablePreferencesRenderer);
const consolidatedSettings = this._consolidateSettings(editablePreferencesFilteredGroups, defaultPreferencesFilteredGroups);
this._settingsNavigator = new SettingsNavigator(this._searchCriteria.filter ? consolidatedSettings : []);
return consolidatedSettings.length;
}
private _focusPreference(preference: ISetting, preferencesRenderer: IPreferencesRenderer<ISetting>): void {
@@ -507,16 +545,16 @@ class PreferencesRenderers extends Disposable {
}
}
private _updatePreference(key: string, value: any, source: ISetting, preferencesRenderer: IPreferencesRenderer<ISetting>): void {
private _updatePreference(key: string, value: any, source: ISetting, index: number, preferencesRenderer: IPreferencesRenderer<ISetting>): void {
if (preferencesRenderer) {
preferencesRenderer.updatePreference(key, value, source);
preferencesRenderer.updatePreference(key, value, source, index);
}
}
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];
return [...defaultSettings, ...editableSettings];
}
private _flatten(settingsGroups: ISettingsGroup[]): ISetting[] {
@@ -530,7 +568,8 @@ class PreferencesRenderers extends Disposable {
}
public dispose(): void {
dispose(this._disposables);
dispose(this._defaultPreferencesRendererDisposables);
dispose(this._editablePreferencesRendererDisposables);
super.dispose();
}
}
@@ -539,19 +578,31 @@ class SideBySidePreferencesWidget extends Widget {
private dimension: Dimension;
private defaultPreferencesHeader: HTMLElement;
private defaultPreferencesEditor: DefaultPreferencesEditor;
private editablePreferencesEditor: BaseEditor;
private defaultPreferencesEditorContainer: HTMLElement;
private editablePreferencesEditorContainer: HTMLElement;
private settingsTargetsWidget: SettingsTargetsWidget;
private _onFocus: Emitter<void> = new Emitter<void>();
readonly onFocus: Event<void> = this._onFocus.event;
private _onDidSettingsTargetChange: Emitter<SettingsTarget> = new Emitter<SettingsTarget>();
readonly onDidSettingsTargetChange: Event<SettingsTarget> = this._onDidSettingsTargetChange.event;
private lastFocusedEditor: BaseEditor;
private sash: VSash;
constructor(parent: HTMLElement, @IInstantiationService private instantiationService: IInstantiationService, @IThemeService private themeService: IThemeService) {
constructor(
parent: HTMLElement,
@IInstantiationService private instantiationService: IInstantiationService,
@IThemeService private themeService: IThemeService,
@IWorkspaceContextService private workspaceContextService: IWorkspaceContextService,
@IPreferencesService private preferencesService: IPreferencesService,
) {
super();
this.create(parent);
}
@@ -562,6 +613,13 @@ class SideBySidePreferencesWidget extends Widget {
this.defaultPreferencesEditorContainer = DOM.append(parentElement, DOM.$('.default-preferences-editor-container'));
this.defaultPreferencesEditorContainer.style.position = 'absolute';
const defaultPreferencesHeaderContainer = DOM.append(this.defaultPreferencesEditorContainer, DOM.$('.preferences-header-container'));
defaultPreferencesHeaderContainer.style.height = '30px';
defaultPreferencesHeaderContainer.style.marginBottom = '4px';
this.defaultPreferencesHeader = DOM.append(defaultPreferencesHeaderContainer, DOM.$('div.default-preferences-header'));
this.defaultPreferencesHeader.textContent = nls.localize('defaultSettings', "Default Settings");
this.defaultPreferencesEditor = this._register(this.instantiationService.createInstance(DefaultPreferencesEditor));
this.defaultPreferencesEditor.create(new Builder(this.defaultPreferencesEditorContainer));
this.defaultPreferencesEditor.setVisible(true);
@@ -569,6 +627,11 @@ class SideBySidePreferencesWidget extends Widget {
this.editablePreferencesEditorContainer = DOM.append(parentElement, DOM.$('.editable-preferences-editor-container'));
this.editablePreferencesEditorContainer.style.position = 'absolute';
const editablePreferencesHeaderContainer = DOM.append(this.editablePreferencesEditorContainer, DOM.$('.preferences-header-container'));
editablePreferencesHeaderContainer.style.height = '30px';
editablePreferencesHeaderContainer.style.marginBottom = '4px';
this.settingsTargetsWidget = this._register(this.instantiationService.createInstance(SettingsTargetsWidget, editablePreferencesHeaderContainer));
this._register(this.settingsTargetsWidget.onDidTargetChange(target => this._onDidSettingsTargetChange.fire(target)));
this._register(attachStylerCallback(this.themeService, { scrollbarShadow }, colors => {
const shadow = colors.scrollbarShadow ? colors.scrollbarShadow.toString() : null;
@@ -581,15 +644,19 @@ class SideBySidePreferencesWidget extends Widget {
}));
const focusTracker = this._register(DOM.trackFocus(parentElement));
this._register(focusTracker.addFocusListener(() => this._onFocus.fire()));
this._register(focusTracker.onDidFocus(() => this._onFocus.fire()));
}
public setInput(defaultPreferencesEditorInput: DefaultPreferencesEditorInput, editablePreferencesEditorInput: EditorInput, options?: EditorOptions): TPromise<{ defaultPreferencesRenderer: IPreferencesRenderer<ISetting>, editablePreferencesRenderer: IPreferencesRenderer<ISetting> }> {
this.getOrCreateEditablePreferencesEditor(editablePreferencesEditorInput);
this.settingsTargetsWidget.settingsTarget = this.getSettingsTarget(editablePreferencesEditorInput.getResource());
this.dolayout(this.sash.getVerticalSashLeft());
return TPromise.join([this.updateInput(this.defaultPreferencesEditor, defaultPreferencesEditorInput, DefaultSettingsEditorContribution.ID, editablePreferencesEditorInput.getResource(), options),
this.updateInput(this.editablePreferencesEditor, editablePreferencesEditorInput, SettingsEditorContribution.ID, defaultPreferencesEditorInput.getResource(), options)])
.then(([defaultPreferencesRenderer, editablePreferencesRenderer]) => ({ defaultPreferencesRenderer, editablePreferencesRenderer }));
.then(([defaultPreferencesRenderer, editablePreferencesRenderer]) => {
this.defaultPreferencesHeader.textContent = defaultPreferencesRenderer && (<DefaultSettingsEditorModel>defaultPreferencesRenderer.preferencesModel).configurationScope === ConfigurationScope.RESOURCE ? nls.localize('defaultFolderSettings', "Default Folder Settings") : nls.localize('defaultSettings', "Default Settings");
return { defaultPreferencesRenderer, editablePreferencesRenderer };
});
}
public layout(dimension: Dimension): void {
@@ -668,8 +735,26 @@ class SideBySidePreferencesWidget extends Widget {
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));
this.defaultPreferencesEditor.layout(new Dimension(detailsEditorWidth, this.dimension.height - 34 /* height of header container */));
this.editablePreferencesEditor.layout(new Dimension(masterEditorWidth, this.dimension.height - 34 /* height of header container */));
}
private getSettingsTarget(resource: URI): SettingsTarget {
if (this.preferencesService.userSettingsResource.toString() === resource.toString()) {
return ConfigurationTarget.USER;
}
const workspaceSettingsResource = this.preferencesService.workspaceSettingsResource;
if (workspaceSettingsResource && workspaceSettingsResource.toString() === resource.toString()) {
return ConfigurationTarget.WORKSPACE;
}
const folder = this.workspaceContextService.getWorkspaceFolder(resource);
if (folder) {
return folder.uri;
}
return ConfigurationTarget.USER;
}
private disposeEditors(): void {
@@ -689,98 +774,20 @@ class SideBySidePreferencesWidget extends Widget {
}
}
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);
super(DefaultPreferencesEditor.ID, telemetryService, instantiationService, storageService, configurationService, themeService, textFileService, editorGroupService);
}
public createEditorControl(parent: Builder, configuration: IEditorOptions): editorCommon.IEditor {
@@ -793,7 +800,7 @@ export class DefaultPreferencesEditor extends BaseTextEditor {
return editor;
}
private showReadonlyHint(editor: editorCommon.ICommonCodeEditor): void {
private showReadonlyHint(editor: ICodeEditor): void {
const messageController = MessageController.get(editor);
if (!messageController.isVisible()) {
messageController.showMessage(nls.localize('defaultEditorReadonly', "Edit in the right hand side editor to override defaults."), editor.getSelection().getPosition());
@@ -862,7 +869,7 @@ interface ISettingsEditorContribution extends editorCommon.IEditorContribution {
}
abstract class AbstractSettingsEditorContribution extends Disposable {
abstract class AbstractSettingsEditorContribution extends Disposable implements ISettingsEditorContribution {
private preferencesRendererCreationPromise: TPromise<IPreferencesRenderer<ISetting>>;
@@ -938,6 +945,7 @@ abstract class AbstractSettingsEditorContribution extends Disposable {
}
protected abstract _createPreferencesRenderer(): TPromise<IPreferencesRenderer<ISetting>>;
abstract getId(): string;
}
class DefaultSettingsEditorContribution extends AbstractSettingsEditorContribution implements ISettingsEditorContribution {
@@ -961,7 +969,6 @@ class DefaultSettingsEditorContribution extends AbstractSettingsEditorContributi
}
}
@editorContribution
class SettingsEditorContribution extends AbstractSettingsEditorContribution implements ISettingsEditorContribution {
static ID: string = 'editor.contrib.settings';
@@ -1031,6 +1038,8 @@ class SettingsEditorContribution extends AbstractSettingsEditorContribution impl
}
registerEditorContribution(SettingsEditorContribution);
abstract class SettingsCommand extends Command {
protected getPreferencesEditor(accessor: ServicesAccessor): PreferencesEditor {

View File

@@ -6,6 +6,7 @@
import { TPromise } from 'vs/base/common/winjs.base';
import * as nls from 'vs/nls';
import { Delayer } from 'vs/base/common/async';
import * as strings from 'vs/base/common/strings';
import { tail } from 'vs/base/common/arrays';
import { Disposable, IDisposable, dispose } from 'vs/base/common/lifecycle';
import { IAction } from 'vs/base/common/actions';
@@ -16,16 +17,14 @@ import * as editorCommon from 'vs/editor/common/editorCommon';
import { Range, IRange } from 'vs/editor/common/core/range';
import { IConfigurationRegistry, Extensions as ConfigurationExtensions, ConfigurationScope, IConfigurationPropertySchema } from 'vs/platform/configuration/common/configurationRegistry';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IPreferencesService, ISettingsGroup, ISetting, IPreferencesEditorModel, IFilterResult, ISettingsEditorModel, IScoredResults } from 'vs/workbench/parts/preferences/common/preferences';
import { IPreferencesService, ISettingsGroup, ISetting, IPreferencesEditorModel, IFilterResult, ISettingsEditorModel, IScoredResults, IWorkbenchSettingsConfiguration } from 'vs/workbench/parts/preferences/common/preferences';
import { SettingsEditorModel, DefaultSettingsEditorModel, WorkspaceConfigurationEditorModel } from 'vs/workbench/parts/preferences/common/preferencesModels';
import { ICodeEditor, IEditorMouseEvent, MouseTargetType } from 'vs/editor/browser/editorBrowser';
import { IContextMenuService, ContextSubMenu } from 'vs/platform/contextview/browser/contextView';
import { SettingsGroupTitleWidget, EditPreferenceWidget, SettingsHeaderWidget, DefaultSettingsHeaderWidget, FloatingClickWidget } from 'vs/workbench/parts/preferences/browser/preferencesWidgets';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { RangeHighlightDecorations } from 'vs/workbench/common/editor/rangeDecorations';
import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles';
import { RangeHighlightDecorations } from 'vs/workbench/browser/parts/editor/rangeDecorations';
import { IMarkerService, IMarkerData } from 'vs/platform/markers/common/markers';
import { IWorkspaceConfigurationService } from 'vs/workbench/services/configuration/common/configuration';
import { IMessageService, Severity } from 'vs/platform/message/common/message';
import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService';
import { ICursorPositionChangedEvent } from 'vs/editor/common/controller/cursorEvents';
@@ -41,11 +40,11 @@ export interface IPreferencesRenderer<T> extends IDisposable {
onFocusPreference: Event<T>;
onClearFocusPreference: Event<T>;
onUpdatePreference: Event<{ key: string, value: any, source: T }>;
onUpdatePreference?: Event<{ key: string, value: any, source: T, index: number }>;
onTriggeredFuzzy?: Event<void>;
render(): void;
updatePreference(key: string, value: any, source: T): void;
updatePreference(key: string, value: any, source: T, index: number): void;
filterPreferences(filterResult: IFilterResult, fuzzySearchAvailable: boolean): void;
focusPreference(setting: T): void;
clearFocus(setting: T): void;
@@ -62,9 +61,6 @@ export class UserSettingsRenderer extends Disposable implements IPreferencesRend
private _onFocusPreference: Emitter<ISetting> = new Emitter<ISetting>();
public readonly onFocusPreference: Event<ISetting> = this._onFocusPreference.event;
private _onUpdatePreference: Emitter<{ key: string, value: any, source: ISetting }> = new Emitter<{ key: string, value: any, source: ISetting }>();
public readonly onUpdatePreference: Event<{ key: string, value: any, source: ISetting }> = this._onUpdatePreference.event;
private _onClearFocusPreference: Emitter<ISetting> = new Emitter<ISetting>();
public readonly onClearFocusPreference: Event<ISetting> = this._onClearFocusPreference.event;
@@ -73,7 +69,6 @@ export class UserSettingsRenderer extends Disposable implements IPreferencesRend
constructor(protected editor: ICodeEditor, public readonly preferencesModel: SettingsEditorModel,
@IPreferencesService protected preferencesService: IPreferencesService,
@ITelemetryService private telemetryService: ITelemetryService,
@ITextFileService private textFileService: ITextFileService,
@IConfigurationService private configurationService: IConfigurationService,
@IInstantiationService protected instantiationService: IInstantiationService
) {
@@ -81,7 +76,7 @@ export class UserSettingsRenderer extends Disposable implements IPreferencesRend
this.settingHighlighter = this._register(instantiationService.createInstance(SettingHighlighter, editor, this._onFocusPreference, this._onClearFocusPreference));
this.highlightMatchesRenderer = this._register(instantiationService.createInstance(HighlightMatchesRenderer, editor));
this.editSettingActionRenderer = this._register(this.instantiationService.createInstance(EditSettingRenderer, this.editor, this.preferencesModel, this.settingHighlighter));
this._register(this.editSettingActionRenderer.onUpdateSetting(({ key, value, source }) => this.updatePreference(key, value, source)));
this._register(this.editSettingActionRenderer.onUpdateSetting(({ key, value, source, index }) => this.updatePreference(key, value, source, index, true)));
this._register(this.editor.getModel().onDidChangeContent(() => this.modelChangeDelayer.trigger(() => this.onModelChanged())));
this.createHeader();
@@ -107,13 +102,30 @@ export class UserSettingsRenderer extends Disposable implements IPreferencesRend
}
}
public updatePreference(key: string, value: any, source: ISetting): void {
public updatePreference(key: string, value: any, source: ISetting, index: number, fromEditableSettings?: boolean): void {
const data = {
userConfigurationKeys: [key]
};
if (this.filterResult) {
data['query'] = this.filterResult.query;
data['fuzzy'] = !!this.filterResult.metadata;
data['duration'] = this.filterResult.metadata && this.filterResult.metadata.duration;
data['index'] = index;
data['editableSide'] = !!fromEditableSettings;
}
/* __GDPR__
"defaultSettingsActions.copySetting" : {
"userConfigurationKeys" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
"userConfigurationKeys" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
"query" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
"fuzzy" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
"duration" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
"index" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
"editableSide" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
}
*/
this.telemetryService.publicLog('defaultSettingsActions.copySetting', { userConfigurationKeys: [key] });
this.telemetryService.publicLog('defaultSettingsActions.copySetting', data);
const overrideIdentifier = source.overrideOf ? overrideIdentifierFromKey(source.overrideOf.key) : null;
const resource = this.preferencesModel.uri;
this.configurationService.updateValue(key, value, { overrideIdentifier, resource }, this.preferencesModel.configurationTarget)
@@ -162,6 +174,7 @@ export class UserSettingsRenderer extends Disposable implements IPreferencesRend
const s = this.getSetting(setting);
if (s) {
this.settingHighlighter.highlight(s, true);
this.editor.setPosition({ lineNumber: s.keyRange.startLineNumber, column: s.keyRange.startColumn });
} else {
this.settingHighlighter.clear(true);
}
@@ -180,11 +193,10 @@ export class WorkspaceSettingsRenderer extends UserSettingsRenderer implements I
constructor(editor: ICodeEditor, preferencesModel: SettingsEditorModel,
@IPreferencesService preferencesService: IPreferencesService,
@ITelemetryService telemetryService: ITelemetryService,
@ITextFileService textFileService: ITextFileService,
@IConfigurationService configurationService: IConfigurationService,
@IInstantiationService instantiationService: IInstantiationService
) {
super(editor, preferencesModel, preferencesService, telemetryService, textFileService, configurationService, instantiationService);
super(editor, preferencesModel, preferencesService, telemetryService, configurationService, instantiationService);
this.unsupportedSettingsRenderer = this._register(instantiationService.createInstance(UnsupportedSettingsRenderer, editor, preferencesModel));
this.workspaceConfigurationRenderer = this._register(instantiationService.createInstance(WorkspaceConfigurationRenderer, editor, preferencesModel));
}
@@ -207,11 +219,10 @@ export class FolderSettingsRenderer extends UserSettingsRenderer implements IPre
constructor(editor: ICodeEditor, preferencesModel: SettingsEditorModel,
@IPreferencesService preferencesService: IPreferencesService,
@ITelemetryService telemetryService: ITelemetryService,
@ITextFileService textFileService: ITextFileService,
@IConfigurationService configurationService: IConfigurationService,
@IInstantiationService instantiationService: IInstantiationService
) {
super(editor, preferencesModel, preferencesService, telemetryService, textFileService, configurationService, instantiationService);
super(editor, preferencesModel, preferencesService, telemetryService, configurationService, instantiationService);
this.unsupportedSettingsRenderer = this._register(instantiationService.createInstance(UnsupportedSettingsRenderer, editor, preferencesModel));
}
@@ -236,8 +247,8 @@ export class DefaultSettingsRenderer extends Disposable implements IPreferencesR
private editSettingActionRenderer: EditSettingRenderer;
private feedbackWidgetRenderer: FeedbackWidgetRenderer;
private _onUpdatePreference: Emitter<{ key: string, value: any, source: ISetting }> = new Emitter<{ key: string, value: any, source: ISetting }>();
public readonly onUpdatePreference: Event<{ key: string, value: any, source: ISetting }> = this._onUpdatePreference.event;
private _onUpdatePreference: Emitter<{ key: string, value: any, source: ISetting, index: number }> = new Emitter<{ key: string, value: any, source: ISetting, index: number }>();
public readonly onUpdatePreference: Event<{ key: string, value: any, source: ISetting, index: number }> = this._onUpdatePreference.event;
private _onFocusPreference: Emitter<ISetting> = new Emitter<ISetting>();
public readonly onFocusPreference: Event<ISetting> = this._onFocusPreference.event;
@@ -251,7 +262,6 @@ export class DefaultSettingsRenderer extends Disposable implements IPreferencesR
constructor(protected editor: ICodeEditor, public readonly preferencesModel: DefaultSettingsEditorModel,
@IPreferencesService protected preferencesService: IPreferencesService,
@IWorkbenchEditorService private editorService: IWorkbenchEditorService,
@IInstantiationService protected instantiationService: IInstantiationService
) {
super();
@@ -261,14 +271,12 @@ export class DefaultSettingsRenderer extends Disposable implements IPreferencesR
this.filteredMatchesRenderer = this._register(instantiationService.createInstance(FilteredMatchesRenderer, editor));
this.editSettingActionRenderer = this._register(instantiationService.createInstance(EditSettingRenderer, editor, preferencesModel, this.settingHighlighter));
this.feedbackWidgetRenderer = this._register(instantiationService.createInstance(FeedbackWidgetRenderer, editor));
const parenthesisHidingRenderer = this._register(instantiationService.createInstance(StaticContentHidingRenderer, editor, preferencesModel));
this.hiddenAreasRenderer = this._register(instantiationService.createInstance(HiddenAreasRenderer, editor, [this.settingsGroupTitleRenderer, this.filteredMatchesRenderer, parenthesisHidingRenderer]));
this._register(this.editSettingActionRenderer.onUpdateSetting(e => this._onUpdatePreference.fire(e)));
const parenthesisHidingRenderer = this._register(instantiationService.createInstance(StaticContentHidingRenderer, editor, preferencesModel.settingsGroups));
const hiddenAreasProviders = [this.settingsGroupTitleRenderer, this.filteredMatchesRenderer, parenthesisHidingRenderer];
this.hiddenAreasRenderer = this._register(instantiationService.createInstance(HiddenAreasRenderer, editor, hiddenAreasProviders));
this._register(this.settingsGroupTitleRenderer.onHiddenAreasChanged(() => this.hiddenAreasRenderer.render()));
this._register(preferencesModel.onDidChangeGroups(() => this.render()));
this.onTriggeredFuzzy = this.settingsHeaderRenderer.onClick;
}
@@ -286,7 +294,6 @@ export class DefaultSettingsRenderer extends Disposable implements IPreferencesR
this.settingsGroupTitleRenderer.render(this.preferencesModel.settingsGroups);
this.editSettingActionRenderer.render(this.preferencesModel.settingsGroups, this._associatedPreferencesModel);
this.feedbackWidgetRenderer.render(null);
this.hiddenAreasRenderer.render();
this.settingHighlighter.clear(true);
this.settingsGroupTitleRenderer.showGroup(0);
this.hiddenAreasRenderer.render();
@@ -356,10 +363,6 @@ export class DefaultSettingsRenderer extends Disposable implements IPreferencesR
this.settingHighlighter.clear(true);
}
public collapseAll() {
this.settingsGroupTitleRenderer.collapseAll();
}
public updatePreference(key: string, value: any, source: ISetting): void {
}
}
@@ -370,7 +373,7 @@ export interface HiddenAreasProvider {
export class StaticContentHidingRenderer extends Disposable implements HiddenAreasProvider {
constructor(private editor: ICodeEditor, private settingsGroups: ISettingsGroup[]
constructor(private editor: ICodeEditor, private settingsEditorModel: ISettingsEditorModel
) {
super();
}
@@ -379,7 +382,8 @@ export class StaticContentHidingRenderer extends Disposable implements HiddenAre
const model = this.editor.getModel();
// Hide extra chars for "search results" and "commonly used" groups
const lastGroup = tail(this.settingsGroups);
const settingsGroups = this.settingsEditorModel.settingsGroups;
const lastGroup = tail(settingsGroups);
return [
{
startLineNumber: 1,
@@ -388,10 +392,10 @@ export class StaticContentHidingRenderer extends Disposable implements HiddenAre
endColumn: model.getLineMaxColumn(2)
},
{
startLineNumber: this.settingsGroups[0].range.endLineNumber + 1,
startColumn: model.getLineMinColumn(this.settingsGroups[0].range.endLineNumber + 1),
endLineNumber: this.settingsGroups[0].range.endLineNumber + 4,
endColumn: model.getLineMaxColumn(this.settingsGroups[0].range.endLineNumber + 4)
startLineNumber: settingsGroups[0].range.endLineNumber + 1,
startColumn: model.getLineMinColumn(settingsGroups[0].range.endLineNumber + 1),
endLineNumber: settingsGroups[0].range.endLineNumber + 4,
endColumn: model.getLineMaxColumn(settingsGroups[0].range.endLineNumber + 4)
},
{
startLineNumber: lastGroup.range.endLineNumber + 1,
@@ -415,10 +419,9 @@ class DefaultSettingsHeaderRenderer extends Disposable {
private settingsHeaderWidget: DefaultSettingsHeaderWidget;
public onClick: Event<void>;
constructor(private editor: ICodeEditor, scope: ConfigurationScope) {
constructor(editor: ICodeEditor, scope: ConfigurationScope) {
super();
const title = scope === ConfigurationScope.RESOURCE ? nls.localize('defaultFolderSettingsTitle', "Default Folder Settings") : nls.localize('defaultSettingsTitle', "Default Settings");
this.settingsHeaderWidget = this._register(new DefaultSettingsHeaderWidget(editor, title));
this.settingsHeaderWidget = this._register(new DefaultSettingsHeaderWidget(editor, ''));
this.onClick = this.settingsHeaderWidget.onClick;
}
@@ -432,7 +435,7 @@ class DefaultSettingsHeaderRenderer extends Disposable {
export class SettingsGroupTitleRenderer extends Disposable implements HiddenAreasProvider {
private _onHiddenAreasChanged: Emitter<void> = new Emitter<void>();
get onHiddenAreasChanged(): Event<void> { return this._onHiddenAreasChanged.event; };
get onHiddenAreasChanged(): Event<void> { return this._onHiddenAreasChanged.event; }
private settingsGroups: ISettingsGroup[];
private hiddenGroups: ISettingsGroup[] = [];
@@ -494,15 +497,6 @@ export class SettingsGroupTitleRenderer extends Disposable implements HiddenArea
}
}
public collapseAll() {
this.editor.setPosition({ lineNumber: 1, column: 1 });
this.hiddenGroups = this.settingsGroups.slice();
for (const groupTitleWidget of this.settingsGroupTitleWidgets) {
groupTitleWidget.toggleCollapse(true);
}
this._onHiddenAreasChanged.fire();
}
private onToggled(collapsed: boolean, group: ISettingsGroup) {
const index = this.hiddenGroups.indexOf(group);
if (collapsed) {
@@ -530,8 +524,7 @@ export class SettingsGroupTitleRenderer extends Disposable implements HiddenArea
export class HiddenAreasRenderer extends Disposable {
constructor(private editor: ICodeEditor, private hiddenAreasProviders: HiddenAreasProvider[],
@IInstantiationService private instantiationService: IInstantiationService
constructor(private editor: ICodeEditor, private hiddenAreasProviders: HiddenAreasProvider[]
) {
super();
}
@@ -551,7 +544,12 @@ export class HiddenAreasRenderer extends Disposable {
}
export class FeedbackWidgetRenderer extends Disposable {
private static COMMENT_TEXT = 'Modify the below results to match your expectations. Assign scores to indicate their relevance. Replace this comment with any text feedback.';
private static readonly DEFAULT_COMMENT_TEXT = 'Replace this comment with any text feedback.';
private static readonly INSTRUCTION_TEXT = [
'// Modify the "resultScores" section to contain only your expected results. Assign scores to indicate their relevance.',
'// Results present in "resultScores" will be automatically "boosted" for this query, if they are not already at the top of the result set.',
'// Add phrase pairs to the "alts" section to have them considered to be synonyms in queries.'
].join('\n');
private _feedbackWidget: FloatingClickWidget;
private _currentResult: IFilterResult;
@@ -560,14 +558,17 @@ export class FeedbackWidgetRenderer extends Disposable {
@IInstantiationService private instantiationService: IInstantiationService,
@IWorkbenchEditorService private editorService: IWorkbenchEditorService,
@ITelemetryService private telemetryService: ITelemetryService,
@IMessageService private messageService: IMessageService
@IMessageService private messageService: IMessageService,
@IEnvironmentService private environmentService: IEnvironmentService,
@IConfigurationService private configurationService: IConfigurationService
) {
super();
}
public render(result: IFilterResult): void {
const workbenchSettings = this.configurationService.getValue<IWorkbenchSettingsConfiguration>().workbench.settings;
this._currentResult = result;
if (result && result.metadata) {
if (result && result.metadata && workbenchSettings.enableNaturalLanguageSearchFeedback) {
this.showWidget();
} else if (this._feedbackWidget) {
this.disposeWidget();
@@ -583,24 +584,29 @@ export class FeedbackWidgetRenderer extends Disposable {
}
private getFeedback(): void {
if (!this.telemetryService.isOptedIn) {
if (!this.telemetryService.isOptedIn && this.environmentService.appQuality) {
this.messageService.show(Severity.Error, 'Can\'t send feedback, user is opted out of telemetry');
return;
}
const result = this._currentResult;
const actualResultNames = Object.keys(result.metadata.scoredResults);
const actualResults = result.metadata.scoredResults;
const actualResultNames = Object.keys(actualResults);
const feedbackQuery = {};
feedbackQuery['comment'] = FeedbackWidgetRenderer.COMMENT_TEXT;
const feedbackQuery: any = {};
feedbackQuery['comment'] = FeedbackWidgetRenderer.DEFAULT_COMMENT_TEXT;
feedbackQuery['queryString'] = result.query;
feedbackQuery['resultScores'] = {};
actualResultNames.forEach(settingKey => {
feedbackQuery['resultScores'][settingKey] = 10;
});
feedbackQuery['alts'] = [];
const contents = JSON.stringify(feedbackQuery, undefined, ' ');
this.editorService.openEditor({ contents, language: 'json' }, /*sideBySide=*/true).then(feedbackEditor => {
const contents = FeedbackWidgetRenderer.INSTRUCTION_TEXT + '\n' +
JSON.stringify(feedbackQuery, undefined, ' ') + '\n\n' +
actualResultNames.map(name => `// ${name}: ${result.metadata.scoredResults[name]}`).join('\n');
this.editorService.openEditor({ contents, language: 'jsonc' }, /*sideBySide=*/true).then(feedbackEditor => {
const sendFeedbackWidget = this._register(this.instantiationService.createInstance(FloatingClickWidget, feedbackEditor.getControl(), 'Send feedback', null));
sendFeedbackWidget.render();
@@ -617,7 +623,9 @@ export class FeedbackWidgetRenderer extends Disposable {
private sendFeedback(feedbackEditor: ICodeEditor, result: IFilterResult, actualResults: IScoredResults): TPromise<void> {
const model = feedbackEditor.getModel();
const expectedQueryLines = model.getLinesContent();
const expectedQueryLines = model.getLinesContent()
.filter(line => !strings.startsWith(line, '//'));
let expectedQuery: any;
try {
expectedQuery = JSON.parse(expectedQueryLines.join('\n'));
@@ -626,7 +634,17 @@ export class FeedbackWidgetRenderer extends Disposable {
return TPromise.wrapError(new Error('Invalid JSON: ' + e.message));
}
const userComment = expectedQuery.comment === FeedbackWidgetRenderer.COMMENT_TEXT ? undefined : expectedQuery.comment;
const userComment = expectedQuery.comment === FeedbackWidgetRenderer.DEFAULT_COMMENT_TEXT ? undefined : expectedQuery.comment;
// validate alts
if (!this.validateAlts(expectedQuery.alts)) {
return TPromise.wrapError(new Error('alts must be an array of 2-element string arrays'));
}
const altsAdded = expectedQuery.alts && expectedQuery.alts.length;
const alts = altsAdded ? expectedQuery.alts : undefined;
const workbenchSettings = this.configurationService.getValue<IWorkbenchSettingsConfiguration>().workbench.settings;
const autoIngest = workbenchSettings.naturalLanguageSearchAutoIngestFeedback;
/* __GDPR__
"settingsSearchResultFeedback" : {
@@ -646,10 +664,33 @@ export class FeedbackWidgetRenderer extends Disposable {
expectedResults: expectedQuery.resultScores,
url: result.metadata.remoteUrl,
duration: result.metadata.duration,
timestamp: result.metadata.timestamp
timestamp: result.metadata.timestamp,
buildNumber: this.environmentService.settingsSearchBuildId,
alts,
autoIngest
});
}
private validateAlts(alts?: string[][]): boolean {
if (!alts) {
return true;
}
if (!Array.isArray(alts)) {
return false;
}
if (!alts.length) {
return true;
}
if (!alts.every(altPair => Array.isArray(altPair) && altPair.length === 2 && typeof altPair[0] === 'string' && typeof altPair[1] === 'string')) {
return false;
}
return true;
}
private disposeWidget(): void {
if (this._feedbackWidget) {
this._feedbackWidget.dispose();
@@ -669,8 +710,7 @@ export class FilteredMatchesRenderer extends Disposable implements HiddenAreasPr
private decorationIds: string[] = [];
public hiddenAreas: IRange[] = [];
constructor(private editor: ICodeEditor,
@IInstantiationService private instantiationService: IInstantiationService
constructor(private editor: ICodeEditor
) {
super();
}
@@ -773,8 +813,7 @@ export class HighlightMatchesRenderer extends Disposable {
private decorationIds: string[] = [];
constructor(private editor: ICodeEditor,
@IInstantiationService private instantiationService: IInstantiationService
constructor(private editor: ICodeEditor
) {
super();
}
@@ -791,7 +830,7 @@ export class HighlightMatchesRenderer extends Disposable {
}
}
private static _FIND_MATCH = ModelDecorationOptions.register({
private static readonly _FIND_MATCH = ModelDecorationOptions.register({
stickiness: editorCommon.TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges,
className: 'findMatch'
});
@@ -813,21 +852,24 @@ export class HighlightMatchesRenderer extends Disposable {
}
}
interface IIndexedSetting extends ISetting {
index: number;
}
class EditSettingRenderer extends Disposable {
private editPreferenceWidgetForCusorPosition: EditPreferenceWidget<ISetting>;
private editPreferenceWidgetForMouseMove: EditPreferenceWidget<ISetting>;
private editPreferenceWidgetForCusorPosition: EditPreferenceWidget<IIndexedSetting>;
private editPreferenceWidgetForMouseMove: EditPreferenceWidget<IIndexedSetting>;
private settingsGroups: ISettingsGroup[];
public associatedPreferencesModel: IPreferencesEditorModel<ISetting>;
private toggleEditPreferencesForMouseMoveDelayer: Delayer<void>;
private _onUpdateSetting: Emitter<{ key: string, value: any, source: ISetting }> = new Emitter<{ key: string, value: any, source: ISetting }>();
public readonly onUpdateSetting: Event<{ key: string, value: any, source: ISetting }> = this._onUpdateSetting.event;
private _onUpdateSetting: Emitter<{ key: string, value: any, source: ISetting, index: number }> = new Emitter<{ key: string, value: any, source: ISetting, index: number }>();
public readonly onUpdateSetting: Event<{ key: string, value: any, source: ISetting, index: number }> = this._onUpdateSetting.event;
constructor(private editor: ICodeEditor, private masterSettingsModel: ISettingsEditorModel,
private settingHighlighter: SettingHighlighter,
@IPreferencesService private preferencesService: IPreferencesService,
@IInstantiationService private instantiationService: IInstantiationService,
@IContextMenuService private contextMenuService: IContextMenuService
) {
@@ -885,7 +927,7 @@ class EditSettingRenderer extends Disposable {
return;
}
this.settingHighlighter.clear();
this.toggleEditPreferencesForMouseMoveDelayer.trigger(() => this.toggleEidtPreferenceWidgetForMouseMove(mouseMoveEvent));
this.toggleEditPreferencesForMouseMoveDelayer.trigger(() => this.toggleEditPreferenceWidgetForMouseMove(mouseMoveEvent));
}
private getEditPreferenceWidgetUnderMouse(mouseMoveEvent: IEditorMouseEvent): EditPreferenceWidget<ISetting> {
@@ -901,7 +943,7 @@ class EditSettingRenderer extends Disposable {
return null;
}
private toggleEidtPreferenceWidgetForMouseMove(mouseMoveEvent: IEditorMouseEvent): void {
private toggleEditPreferenceWidgetForMouseMove(mouseMoveEvent: IEditorMouseEvent): void {
const settings = mouseMoveEvent.target.position ? this.getSettings(mouseMoveEvent.target.position.lineNumber) : null;
if (settings && settings.length) {
this.showEditPreferencesWidget(this.editPreferenceWidgetForMouseMove, settings);
@@ -910,7 +952,7 @@ class EditSettingRenderer extends Disposable {
}
}
private showEditPreferencesWidget(editPreferencesWidget: EditPreferenceWidget<ISetting>, settings: ISetting[]) {
private showEditPreferencesWidget(editPreferencesWidget: EditPreferenceWidget<ISetting>, settings: IIndexedSetting[]) {
const line = settings[0].valueRange.startLineNumber;
if (this.editor.getConfiguration().viewInfo.glyphMargin && this.marginFreeFromOtherDecorations(line)) {
editPreferencesWidget.show(line, nls.localize('editTtile', "Edit"), settings);
@@ -931,7 +973,7 @@ class EditSettingRenderer extends Disposable {
return true;
}
private getSettings(lineNumber: number): ISetting[] {
private getSettings(lineNumber: number): IIndexedSetting[] {
const configurationMap = this.getConfigurationsMap();
return this.getSettingsAtLineNumber(lineNumber).filter(setting => {
let configurationNode = configurationMap[setting.key];
@@ -956,7 +998,10 @@ class EditSettingRenderer extends Disposable {
});
}
private getSettingsAtLineNumber(lineNumber: number): ISetting[] {
private getSettingsAtLineNumber(lineNumber: number): IIndexedSetting[] {
// index of setting, across all groups/sections
let index = 0;
const settings = [];
for (const group of this.settingsGroups) {
if (group.range.startLineNumber > lineNumber) {
@@ -973,13 +1018,15 @@ class EditSettingRenderer extends Disposable {
// Only one level because override settings cannot have override settings
for (const overrideSetting of setting.overrides) {
if (lineNumber >= overrideSetting.range.startLineNumber && lineNumber <= overrideSetting.range.endLineNumber) {
settings.push(overrideSetting);
settings.push({ ...overrideSetting, index });
}
}
} else {
settings.push(setting);
settings.push({ ...setting, index });
}
}
index++;
}
}
}
@@ -991,7 +1038,7 @@ class EditSettingRenderer extends Disposable {
this.settingHighlighter.highlight(editPreferenceWidget.preferences[0]);
}
private onEditSettingClicked(editPreferenceWidget: EditPreferenceWidget<ISetting>, e: IEditorMouseEvent): void {
private onEditSettingClicked(editPreferenceWidget: EditPreferenceWidget<IIndexedSetting>, e: IEditorMouseEvent): void {
const anchor = { x: e.event.posx, y: e.event.posy + 10 };
const actions = this.getSettings(editPreferenceWidget.getLine()).length === 1 ? this.getActions(editPreferenceWidget.preferences[0], this.getConfigurationsMap()[editPreferenceWidget.preferences[0].key])
: editPreferenceWidget.preferences.map(setting => new ContextSubMenu(setting.key, this.getActions(setting, this.getConfigurationsMap()[setting.key])));
@@ -1005,7 +1052,7 @@ class EditSettingRenderer extends Disposable {
return Registry.as<IConfigurationRegistry>(ConfigurationExtensions.Configuration).getConfigurationProperties();
}
private getActions(setting: ISetting, jsonSchema: IJSONSchema): IAction[] {
private getActions(setting: IIndexedSetting, jsonSchema: IJSONSchema): IAction[] {
if (jsonSchema.type === 'boolean') {
return [<IAction>{
id: 'truthyValue',
@@ -1032,7 +1079,7 @@ class EditSettingRenderer extends Disposable {
return this.getDefaultActions(setting);
}
private getDefaultActions(setting: ISetting): IAction[] {
private getDefaultActions(setting: IIndexedSetting): IAction[] {
if (this.isDefaultSettings()) {
const settingInOtherModel = this.associatedPreferencesModel.getPreference(setting.key);
return [<IAction>{
@@ -1045,8 +1092,8 @@ class EditSettingRenderer extends Disposable {
return [];
}
private updateSetting(key: string, value: any, source: ISetting): void {
this._onUpdateSetting.fire({ key, value, source });
private updateSetting(key: string, value: any, source: IIndexedSetting): void {
this._onUpdateSetting.fire({ key, value, source, index: source.index });
}
}
@@ -1056,7 +1103,7 @@ class SettingHighlighter extends Disposable {
private volatileHighlighter: RangeHighlightDecorations;
private highlightedSetting: ISetting;
constructor(private editor: editorCommon.ICommonCodeEditor, private focusEventEmitter: Emitter<ISetting>, private clearFocusEventEmitter: Emitter<ISetting>,
constructor(private editor: ICodeEditor, private focusEventEmitter: Emitter<ISetting>, private clearFocusEventEmitter: Emitter<ISetting>,
@IInstantiationService instantiationService: IInstantiationService
) {
super();
@@ -1096,9 +1143,8 @@ class UnsupportedSettingsRenderer extends Disposable {
private renderingDelayer: Delayer<void> = new Delayer<void>(200);
constructor(
private editor: editorCommon.ICommonCodeEditor,
private editor: ICodeEditor,
private settingsEditorModel: SettingsEditorModel,
@IWorkspaceConfigurationService private configurationService: IWorkspaceConfigurationService,
@IMarkerService private markerService: IMarkerService,
@IEnvironmentService private environmentService: IEnvironmentService
) {
@@ -1174,14 +1220,14 @@ class UnsupportedSettingsRenderer extends Disposable {
super.dispose();
}
private static _DIM_CONFIGUARATION_ = ModelDecorationOptions.register({
private static readonly _DIM_CONFIGUARATION_ = ModelDecorationOptions.register({
stickiness: editorCommon.TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges,
inlineClassName: 'dim-configuration',
beforeContentClassName: 'unsupportedWorkbenhSettingInfo',
hoverMessage: new MarkdownString().appendText(nls.localize('unsupportedWorkbenchSetting', "This setting cannot be applied now. It will be applied when you open this folder directly."))
});
private static _DIM_CONFIGUARATION_DEV_MODE = ModelDecorationOptions.register({
private static readonly _DIM_CONFIGUARATION_DEV_MODE = ModelDecorationOptions.register({
stickiness: editorCommon.TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges,
inlineClassName: 'dim-configuration',
beforeContentClassName: 'unsupportedWorkbenhSettingInfo',
@@ -1194,7 +1240,7 @@ class WorkspaceConfigurationRenderer extends Disposable {
private decorationIds: string[] = [];
private renderingDelayer: Delayer<void> = new Delayer<void>(200);
constructor(private editor: editorCommon.ICommonCodeEditor, private workspaceSettingsEditorModel: SettingsEditorModel,
constructor(private editor: ICodeEditor, private workspaceSettingsEditorModel: SettingsEditorModel,
@IWorkspaceContextService private workspaceContextService: IWorkspaceContextService
) {
super();
@@ -1224,7 +1270,7 @@ class WorkspaceConfigurationRenderer extends Disposable {
}
}
private static _DIM_CONFIGURATION_ = ModelDecorationOptions.register({
private static readonly _DIM_CONFIGURATION_ = ModelDecorationOptions.register({
stickiness: editorCommon.TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges,
inlineClassName: 'dim-configuration'
});

View File

@@ -1,326 +0,0 @@
/*---------------------------------------------------------------------------------------------
* 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 Event, { Emitter } from 'vs/base/common/event';
import { ISettingsEditorModel, IFilterResult, ISetting, ISettingsGroup, IWorkbenchSettingsConfiguration, IFilterMetadata } from 'vs/workbench/parts/preferences/common/preferences';
import { IRange, Range } from 'vs/editor/common/core/range';
import { distinct } from 'vs/base/common/arrays';
import * as strings from 'vs/base/common/strings';
import { IJSONSchema } from 'vs/base/common/jsonSchema';
import { Registry } from 'vs/platform/registry/common/platform';
import { IConfigurationRegistry, Extensions } from 'vs/platform/configuration/common/configurationRegistry';
import { IMatch, or, matchesContiguousSubString, matchesPrefix, matchesCamelCase, matchesWords } from 'vs/base/common/filters';
import { IWorkspaceConfigurationService } from 'vs/workbench/services/configuration/common/configuration';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
export interface IEndpointDetails {
urlBase: string;
key: string;
boost: number;
}
export class PreferencesSearchProvider {
private _onRemoteSearchEnablementChanged = new Emitter<boolean>();
public onRemoteSearchEnablementChanged: Event<boolean> = this._onRemoteSearchEnablementChanged.event;
constructor(
@IWorkspaceConfigurationService private configurationService: IWorkspaceConfigurationService,
@IEnvironmentService private environmentService: IEnvironmentService
) {
configurationService.onDidChangeConfiguration(() => this._onRemoteSearchEnablementChanged.fire(this.remoteSearchEnabled));
}
get remoteSearchEnabled(): boolean {
if (this.environmentService.appQuality === 'stable') {
return false;
}
const endpoint = this.endpoint;
return !!endpoint.urlBase && !!endpoint.key;
}
get endpoint(): IEndpointDetails {
const workbenchSettings = this.configurationService.getConfiguration<IWorkbenchSettingsConfiguration>().workbench.settings;
return {
urlBase: workbenchSettings.experimentalFuzzySearchEndpoint,
key: workbenchSettings.experimentalFuzzySearchKey,
boost: workbenchSettings.experimentalFuzzySearchBoost
};
}
startSearch(filter: string, remote: boolean): PreferencesSearchModel {
return new PreferencesSearchModel(this, filter, remote);
}
}
export class PreferencesSearchModel {
private _localProvider: LocalSearchProvider;
private _remoteProvider: RemoteSearchProvider;
constructor(private provider: PreferencesSearchProvider, private filter: string, remote: boolean) {
this._localProvider = new LocalSearchProvider(filter);
if (remote && filter) {
this._remoteProvider = new RemoteSearchProvider(filter, this.provider.endpoint);
}
}
filterPreferences(preferencesModel: ISettingsEditorModel): TPromise<IFilterResult> {
if (!this.filter) {
return TPromise.wrap(null);
}
if (this._remoteProvider) {
return this._remoteProvider.filterPreferences(preferencesModel).then(null, err => {
return this._localProvider.filterPreferences(preferencesModel);
});
} else {
return this._localProvider.filterPreferences(preferencesModel);
}
}
}
class LocalSearchProvider {
private _filter: string;
constructor(filter: string) {
this._filter = filter;
}
filterPreferences(preferencesModel: ISettingsEditorModel): TPromise<IFilterResult> {
const regex = strings.createRegExp(this._filter, false, { global: true });
const groupFilter = (group: ISettingsGroup) => {
return regex.test(group.title);
};
const settingFilter = (setting: ISetting) => {
return new SettingMatches(this._filter, setting, (filter, setting) => preferencesModel.findValueMatches(filter, setting)).matches;
};
return TPromise.wrap(preferencesModel.filterSettings(this._filter, groupFilter, settingFilter));
}
}
class RemoteSearchProvider {
private _filter: string;
private _remoteSearchP: TPromise<IFilterMetadata>;
constructor(filter: string, endpoint: IEndpointDetails) {
this._filter = filter;
this._remoteSearchP = filter ? getSettingsFromBing(filter, endpoint) : TPromise.wrap(null);
}
filterPreferences(preferencesModel: ISettingsEditorModel): TPromise<IFilterResult> {
return this._remoteSearchP.then(remoteResult => {
const settingFilter = (setting: ISetting) => {
if (!!remoteResult.scoredResults[setting.key]) {
const settingMatches = new SettingMatches(this._filter, setting, (filter, setting) => preferencesModel.findValueMatches(filter, setting)).matches;
if (settingMatches.length) {
return settingMatches;
} else {
return [new Range(setting.keyRange.startLineNumber, setting.keyRange.startColumn, setting.keyRange.endLineNumber, setting.keyRange.startColumn)];
}
} else {
return null;
}
};
if (remoteResult) {
const sortedNames = Object.keys(remoteResult.scoredResults).sort((a, b) => remoteResult.scoredResults[b] - remoteResult.scoredResults[a]);
const result = preferencesModel.filterSettings(this._filter, group => null, settingFilter, sortedNames);
result.metadata = remoteResult;
return result;
} else {
return null;
}
});
}
}
function getSettingsFromBing(filter: string, endpoint: IEndpointDetails): TPromise<IFilterMetadata> {
const url = prepareUrl(filter, endpoint);
console.log('fetching: ' + url);
const start = Date.now();
const p = fetch(url, {
headers: new Headers({
'User-Agent': 'request',
'Content-Type': 'application/json; charset=utf-8',
'api-key': endpoint.key
})
})
.then(r => r.json())
.then(result => {
const timestamp = Date.now();
const duration = timestamp - start;
console.log('time: ' + duration / 1000);
const suggestions = (result.value || [])
.map(r => ({
name: r.setting || r.Setting,
score: r['@search.score']
}));
const scoredResults = Object.create(null);
suggestions.forEach(s => {
const name = s.name
.replace(/^"/, '')
.replace(/"$/, '');
scoredResults[name] = s.score;
});
return <IFilterMetadata>{
remoteUrl: url,
duration,
timestamp,
scoredResults
};
});
return TPromise.as(p as any);
}
const API_VERSION = 'api-version=2016-09-01-Preview';
const QUERY_TYPE = 'querytype=full';
const SCORING_PROFILE = 'scoringProfile=ranking';
function escapeSpecialChars(query: string): string {
return query.replace(/\./g, ' ')
.replace(/[\\/+\-&|!"~*?:(){}\[\]\^]/g, '\\$&')
.replace(/ /g, ' ') // collapse spaces
.trim();
}
function prepareUrl(query: string, endpoint: IEndpointDetails): string {
query = escapeSpecialChars(query);
const boost = endpoint.boost || 1;
const userQuery = `(${query})^${boost}`;
// Appending Fuzzy after each word.
query = query.replace(/\ +/g, '~ ') + '~';
return `${endpoint.urlBase}?${API_VERSION}&search=${encodeURIComponent(userQuery + ' || ' + query)}&${QUERY_TYPE}&${SCORING_PROFILE}`;
}
class SettingMatches {
private readonly descriptionMatchingWords: Map<string, IRange[]> = new Map<string, IRange[]>();
private readonly keyMatchingWords: Map<string, IRange[]> = new Map<string, IRange[]>();
private readonly valueMatchingWords: Map<string, IRange[]> = new Map<string, IRange[]>();
public readonly matches: IRange[];
constructor(searchString: string, setting: ISetting, private valuesMatcher: (filter: string, setting: ISetting) => IRange[]) {
this.matches = distinct(this._findMatchesInSetting(searchString, setting), (match) => `${match.startLineNumber}_${match.startColumn}_${match.endLineNumber}_${match.endColumn}_`);
}
private _findMatchesInSetting(searchString: string, setting: ISetting): IRange[] {
const result = this._doFindMatchesInSetting(searchString, setting);
if (setting.overrides && setting.overrides.length) {
for (const subSetting of setting.overrides) {
const subSettingMatches = new SettingMatches(searchString, subSetting, this.valuesMatcher);
let words = searchString.split(' ');
const descriptionRanges: IRange[] = this.getRangesForWords(words, this.descriptionMatchingWords, [subSettingMatches.descriptionMatchingWords, subSettingMatches.keyMatchingWords, subSettingMatches.valueMatchingWords]);
const keyRanges: IRange[] = this.getRangesForWords(words, this.keyMatchingWords, [subSettingMatches.descriptionMatchingWords, subSettingMatches.keyMatchingWords, subSettingMatches.valueMatchingWords]);
const subSettingKeyRanges: IRange[] = this.getRangesForWords(words, subSettingMatches.keyMatchingWords, [this.descriptionMatchingWords, this.keyMatchingWords, subSettingMatches.valueMatchingWords]);
const subSettinValueRanges: IRange[] = this.getRangesForWords(words, subSettingMatches.valueMatchingWords, [this.descriptionMatchingWords, this.keyMatchingWords, subSettingMatches.keyMatchingWords]);
result.push(...descriptionRanges, ...keyRanges, ...subSettingKeyRanges, ...subSettinValueRanges);
result.push(...subSettingMatches.matches);
}
}
return result;
}
private _doFindMatchesInSetting(searchString: string, setting: ISetting): IRange[] {
const registry: { [qualifiedKey: string]: IJSONSchema } = Registry.as<IConfigurationRegistry>(Extensions.Configuration).getConfigurationProperties();
const schema: IJSONSchema = registry[setting.key];
let words = searchString.split(' ');
const settingKeyAsWords: string = setting.key.split('.').join(' ');
for (const word of words) {
for (let lineIndex = 0; lineIndex < setting.description.length; lineIndex++) {
const descriptionMatches = matchesWords(word, setting.description[lineIndex], true);
if (descriptionMatches) {
this.descriptionMatchingWords.set(word, descriptionMatches.map(match => this.toDescriptionRange(setting, match, lineIndex)));
}
}
const keyMatches = or(matchesWords, matchesCamelCase)(word, settingKeyAsWords);
if (keyMatches) {
this.keyMatchingWords.set(word, keyMatches.map(match => this.toKeyRange(setting, match)));
}
const valueMatches = typeof setting.value === 'string' ? matchesContiguousSubString(word, setting.value) : null;
if (valueMatches) {
this.valueMatchingWords.set(word, valueMatches.map(match => this.toValueRange(setting, match)));
} else if (schema && schema.enum && schema.enum.some(enumValue => typeof enumValue === 'string' && !!matchesContiguousSubString(word, enumValue))) {
this.valueMatchingWords.set(word, []);
}
}
const descriptionRanges: IRange[] = [];
for (let lineIndex = 0; lineIndex < setting.description.length; lineIndex++) {
const matches = or(matchesContiguousSubString)(searchString, setting.description[lineIndex] || '') || [];
descriptionRanges.push(...matches.map(match => this.toDescriptionRange(setting, match, lineIndex)));
}
if (descriptionRanges.length === 0) {
descriptionRanges.push(...this.getRangesForWords(words, this.descriptionMatchingWords, [this.keyMatchingWords, this.valueMatchingWords]));
}
const keyMatches = or(matchesPrefix, matchesContiguousSubString)(searchString, setting.key);
const keyRanges: IRange[] = keyMatches ? keyMatches.map(match => this.toKeyRange(setting, match)) : this.getRangesForWords(words, this.keyMatchingWords, [this.descriptionMatchingWords, this.valueMatchingWords]);
let valueRanges: IRange[] = [];
if (setting.value && typeof setting.value === 'string') {
const valueMatches = or(matchesPrefix, matchesContiguousSubString)(searchString, setting.value);
valueRanges = valueMatches ? valueMatches.map(match => this.toValueRange(setting, match)) : this.getRangesForWords(words, this.valueMatchingWords, [this.keyMatchingWords, this.descriptionMatchingWords]);
} else {
valueRanges = this.valuesMatcher(searchString, setting);
}
return [...descriptionRanges, ...keyRanges, ...valueRanges];
}
private getRangesForWords(words: string[], from: Map<string, IRange[]>, others: Map<string, IRange[]>[]): IRange[] {
const result: IRange[] = [];
for (const word of words) {
const ranges = from.get(word);
if (ranges) {
result.push(...ranges);
} else if (others.every(o => !o.has(word))) {
return [];
}
}
return result;
}
private toKeyRange(setting: ISetting, match: IMatch): IRange {
return {
startLineNumber: setting.keyRange.startLineNumber,
startColumn: setting.keyRange.startColumn + match.start,
endLineNumber: setting.keyRange.startLineNumber,
endColumn: setting.keyRange.startColumn + match.end
};
}
private toDescriptionRange(setting: ISetting, match: IMatch, lineIndex: number): IRange {
return {
startLineNumber: setting.descriptionRanges[lineIndex].startLineNumber,
startColumn: setting.descriptionRanges[lineIndex].startColumn + match.start,
endLineNumber: setting.descriptionRanges[lineIndex].endLineNumber,
endColumn: setting.descriptionRanges[lineIndex].startColumn + match.end
};
}
private toValueRange(setting: ISetting, match: IMatch): IRange {
return {
startLineNumber: setting.valueRange.startLineNumber,
startColumn: setting.valueRange.startColumn + match.start + 1,
endLineNumber: setting.valueRange.startLineNumber,
endColumn: setting.valueRange.startColumn + match.end + 1
};
}
}

View File

@@ -17,21 +17,19 @@ import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/edi
import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace';
import { IWorkspaceConfigurationService } from 'vs/workbench/services/configuration/common/configuration';
import { Position as EditorPosition, IEditor, IEditorOptions } from 'vs/platform/editor/common/editor';
import { ICommonCodeEditor, IModel } from 'vs/editor/common/editorCommon';
import { IModel } 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 { IMessageService, Severity } from 'vs/platform/message/common/message';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { IPreferencesService, IPreferencesEditorModel, ISetting, getSettingsTargetName, FOLDER_SETTINGS_PATH, DEFAULT_SETTINGS_EDITOR_SETTING } from 'vs/workbench/parts/preferences/common/preferences';
import { SettingsEditorModel, DefaultSettingsEditorModel, DefaultKeybindingsEditorModel, defaultKeybindingsContents, DefaultSettingsModel, WorkspaceConfigurationEditorModel } from 'vs/workbench/parts/preferences/common/preferencesModels';
import { SettingsEditorModel, DefaultSettingsEditorModel, DefaultKeybindingsEditorModel, defaultKeybindingsContents, DefaultSettings, WorkspaceConfigurationEditorModel } 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 { getCodeEditor } from 'vs/editor/browser/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';
@@ -41,6 +39,7 @@ import { ConfigurationScope } from 'vs/platform/configuration/common/configurati
import { ConfigurationTarget } from 'vs/platform/configuration/common/configuration';
import { IModeService } from 'vs/editor/common/services/modeService';
import { parse } from 'vs/base/common/json';
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
const emptyEditableSettingsContent = '{\n}';
@@ -53,9 +52,9 @@ export class PreferencesService extends Disposable implements IPreferencesServic
private _onDispose: Emitter<void> = new Emitter<void>();
private _defaultSettingsUriCounter = 0;
private _defaultSettingsContentModel: DefaultSettingsModel;
private _defaultSettingsContentModel: DefaultSettings;
private _defaultResourceSettingsUriCounter = 0;
private _defaultResourceSettingsContentModel: DefaultSettingsModel;
private _defaultResourceSettingsContentModel: DefaultSettings;
constructor(
@IWorkbenchEditorService private editorService: IWorkbenchEditorService,
@@ -63,14 +62,11 @@ export class PreferencesService extends Disposable implements IPreferencesServic
@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,
@IExtensionService private extensionService: IExtensionService,
@IKeybindingService keybindingService: IKeybindingService,
@IModelService private modelService: IModelService,
@IJSONEditingService private jsonEditingService: IJSONEditingService,
@@ -97,6 +93,7 @@ export class PreferencesService extends Disposable implements IPreferencesServic
}
readonly defaultKeybindingsResource = URI.from({ scheme: network.Schemas.vscode, authority: 'defaultsettings', path: '/keybindings.json' });
private readonly defaultSettingsRawResource = URI.from({ scheme: network.Schemas.vscode, authority: 'defaultsettings', path: '/defaultSettings.json' });
get userSettingsResource(): URI {
return this.getEditableSettingsURI(ConfigurationTarget.USER);
@@ -112,19 +109,44 @@ export class PreferencesService extends Disposable implements IPreferencesServic
resolveModel(uri: URI): TPromise<IModel> {
if (this.isDefaultSettingsResource(uri) || this.isDefaultResourceSettingsResource(uri)) {
return this.extensionService.onReady()
.then(() => {
const scope = this.isDefaultSettingsResource(uri) ? ConfigurationScope.WINDOW : ConfigurationScope.RESOURCE;
const settingsModel = this.getDefaultSettingsModel(scope);
const mode = this.modeService.getOrCreateMode('json');
const model = this._register(this.modelService.createModel(settingsModel.content, mode, uri));
return model;
});
const scope = this.isDefaultSettingsResource(uri) ? ConfigurationScope.WINDOW : ConfigurationScope.RESOURCE;
const mode = this.modeService.getOrCreateMode('jsonc');
const model = this._register(this.modelService.createModel('', mode, uri));
let defaultSettings: DefaultSettings;
this.configurationService.onDidChangeConfiguration(e => {
if (e.source === ConfigurationTarget.DEFAULT) {
const model = this.modelService.getModel(uri);
if (!model) {
// model has not been given out => nothing to do
return;
}
defaultSettings = this.getDefaultSettings(scope);
this.modelService.updateModel(model, defaultSettings.parse());
defaultSettings._onDidChange.fire();
}
});
// Check if Default settings is already created and updated in above promise
if (!defaultSettings) {
defaultSettings = this.getDefaultSettings(scope);
this.modelService.updateModel(model, defaultSettings.parse());
}
return TPromise.as(model);
}
if (this.defaultSettingsRawResource.toString() === uri.toString()) {
let defaultSettings: DefaultSettings = this.getDefaultSettings(ConfigurationScope.WINDOW);
const mode = this.modeService.getOrCreateMode('jsonc');
const model = this._register(this.modelService.createModel(defaultSettings.raw, mode, uri));
return TPromise.as(model);
}
if (this.defaultKeybindingsResource.toString() === uri.toString()) {
const defaultKeybindingsEditorModel = this.instantiationService.createInstance(DefaultKeybindingsEditorModel, uri);
const mode = this.modeService.getOrCreateMode('json');
const mode = this.modeService.getOrCreateMode('jsonc');
const model = this._register(this.modelService.createModel(defaultKeybindingsEditorModel.content, mode, uri));
return TPromise.as(model);
}
@@ -153,6 +175,10 @@ export class PreferencesService extends Disposable implements IPreferencesServic
return TPromise.wrap<IPreferencesEditorModel<any>>(null);
}
openRawDefaultSettings(): TPromise<void> {
return this.editorService.openEditor({ resource: this.defaultSettingsRawResource }, EditorPosition.ONE) as TPromise<any>;
}
openGlobalSettings(options?: IEditorOptions, position?: EditorPosition): TPromise<IEditor> {
return this.doOpenSettings(ConfigurationTarget.USER, this.userSettingsResource, options, position);
}
@@ -290,20 +316,20 @@ export class PreferencesService extends Disposable implements IPreferencesServic
return this.textModelResolverService.createModelReference(defaultSettingsUri)
.then(reference => {
const scope = this.isDefaultSettingsResource(defaultSettingsUri) ? ConfigurationScope.WINDOW : ConfigurationScope.RESOURCE;
return this.instantiationService.createInstance(DefaultSettingsEditorModel, defaultSettingsUri, reference, scope, this.getDefaultSettingsModel(scope).settingsGroups);
return this.instantiationService.createInstance(DefaultSettingsEditorModel, defaultSettingsUri, reference, scope, this.getDefaultSettings(scope));
});
}
private getDefaultSettingsModel(scope: ConfigurationScope): DefaultSettingsModel {
private getDefaultSettings(scope: ConfigurationScope): DefaultSettings {
switch (scope) {
case ConfigurationScope.WINDOW:
if (!this._defaultSettingsContentModel) {
this._defaultSettingsContentModel = new DefaultSettingsModel(this.getMostCommonlyUsedSettings(), scope);
this._defaultSettingsContentModel = new DefaultSettings(this.getMostCommonlyUsedSettings(), scope);
}
return this._defaultSettingsContentModel;
case ConfigurationScope.RESOURCE:
if (!this._defaultResourceSettingsContentModel) {
this._defaultResourceSettingsContentModel = new DefaultSettingsModel(this.getMostCommonlyUsedSettings(), scope);
this._defaultResourceSettingsContentModel = new DefaultSettings(this.getMostCommonlyUsedSettings(), scope);
}
return this._defaultResourceSettingsContentModel;
}
@@ -367,22 +393,22 @@ export class PreferencesService extends Disposable implements IPreferencesServic
];
}
private getPosition(language: string, codeEditor: ICommonCodeEditor): TPromise<IPosition> {
private getPosition(language: string, codeEditor: ICodeEditor): 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');
const configuration = this.configurationService.getValue<{ editor: { tabSize: number; insertSpaces: boolean }, files: { eol: string } }>();
const eol = configuration.files && configuration.files.eol;
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);
content = ',' + eol + this.spaces(2, configuration.editor) + eol + this.spaces(1, configuration.editor);
} else {
content = ',' + eol + this.spaces(2, configuration);
content = ',' + eol + this.spaces(2, configuration.editor);
}
const editOperation = EditOperation.insert(new Position(lastSetting.valueRange.endLineNumber, lastSetting.valueRange.endColumn), content);
model.pushEditOperations([], [editOperation], () => []);
@@ -393,7 +419,7 @@ export class PreferencesService extends Disposable implements IPreferencesServic
return this.configurationService.updateValue(languageKey, {}, ConfigurationTarget.USER)
.then(() => {
setting = settingsModel.getPreference(languageKey);
let content = eol + this.spaces(2, configuration) + eol + this.spaces(1, configuration);
let content = eol + this.spaces(2, configuration.editor) + eol + this.spaces(1, configuration.editor);
let editOperation = EditOperation.insert(new Position(setting.valueRange.endLineNumber, setting.valueRange.endColumn - 1), content);
model.pushEditOperations([], [editOperation], () => []);
let lineNumber = setting.valueRange.endLineNumber + 1;

View File

@@ -5,38 +5,36 @@
import { localize } from 'vs/nls';
import URI from 'vs/base/common/uri';
import { Dimension } from 'vs/base/browser/builder';
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 { Disposable, IDisposable, dispose } from 'vs/base/common/lifecycle';
import { Widget } from 'vs/base/browser/ui/widget';
import { Checkbox } from 'vs/base/browser/ui/checkbox/checkbox';
import Event, { Emitter } from 'vs/base/common/event';
import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent';
import { IKeyboardEvent, StandardKeyboardEvent } 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 { ISettingsGroup } from 'vs/workbench/parts/preferences/common/preferences';
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace';
import { IAction, IActionRunner } from 'vs/base/common/actions';
import { attachInputBoxStyler, attachStylerCallback, attachSelectBoxStyler, attachCheckboxStyler } from 'vs/platform/theme/common/styler';
import { IThemeService } from 'vs/platform/theme/common/themeService';
import { IWorkspaceContextService, WorkbenchState, IWorkspaceFolder } from 'vs/platform/workspace/common/workspace';
import { IAction, Action } from 'vs/base/common/actions';
import { attachInputBoxStyler, attachStylerCallback, attachCheckboxStyler } from 'vs/platform/theme/common/styler';
import { IThemeService, registerThemingParticipant, ITheme, ICssStyleCollector } 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 { buttonBackground, buttonForeground, badgeForeground, badgeBackground, contrastBorder, errorForeground, focusBorder, activeContrastBorder } 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 { IMouseEvent } from 'vs/base/browser/mouseEvent';
import { Separator, ActionBar, ActionsOrientation, BaseActionItem } from 'vs/base/browser/ui/actionbar/actionbar';
import { MarkdownString } from 'vs/base/common/htmlContent';
import { ConfigurationTarget } from 'vs/platform/configuration/common/configuration';
import { IMarginData } from 'vs/editor/browser/controller/mouseTarget';
import { render as renderOcticons } from 'vs/base/browser/ui/octiconLabel/octiconLabel';
import { PANEL_ACTIVE_TITLE_FOREGROUND, PANEL_ACTIVE_TITLE_BORDER, PANEL_INACTIVE_TITLE_FOREGROUND } from 'vs/workbench/common/theme';
export class SettingsHeaderWidget extends Widget implements IViewZone {
@@ -91,7 +89,7 @@ export class SettingsHeaderWidget extends Widget implements IViewZone {
const configuration = this.editor.getConfiguration();
this.titleContainer.style.fontSize = configuration.fontInfo.fontSize + 'px';
if (!configuration.contribInfo.folding) {
this.titleContainer.style.paddingLeft = '12px';
this.titleContainer.style.paddingLeft = '6px';
}
}
@@ -112,8 +110,8 @@ export class DefaultSettingsHeaderWidget extends SettingsHeaderWidget {
protected create() {
super.create();
this.linkElement = DOM.append(this.titleContainer, DOM.$('a.settings-header-fuzzy-link'));
this.linkElement.textContent = localize('defaultSettingsFuzzyPrompt', "Try fuzzy search!");
this.linkElement = DOM.append(this.titleContainer, DOM.$('a.settings-header-natural-language-link'));
this.linkElement.textContent = localize('defaultSettingsFuzzyPrompt', "Try natural language search!");
this.onclick(this.linkElement, e => this._onClick.fire());
this.toggleMessage(true);
@@ -178,8 +176,9 @@ export class SettingsGroupTitleWidget extends Widget implements IViewZone {
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._register(focusTracker.onDidFocus(() => this.toggleFocus(true)));
this._register(focusTracker.onDidBlur(() => this.toggleFocus(false)));
this.icon = DOM.append(this.titleContainer, DOM.$('.expand-collapse-icon'));
this.title = DOM.append(this.titleContainer, DOM.$('.title'));
@@ -293,137 +292,249 @@ export class SettingsGroupTitleWidget extends Widget implements IViewZone {
}
}
export class SettingsTargetsWidget extends Widget {
export class FolderSettingsActionItem extends BaseActionItem {
public actionRunner: IActionRunner;
private settingsTargetsContainer: HTMLSelectElement;
private targetLabel: HTMLSelectElement;
private targetDetails: HTMLSelectElement;
private _folder: IWorkspaceFolder;
private _onDidTargetChange: Emitter<URI> = new Emitter<URI>();
public readonly onDidTargetChange: Event<URI> = this._onDidTargetChange.event;
private container: HTMLElement;
private anchorElement: HTMLElement;
private labelElement: HTMLElement;
private detailsElement: HTMLElement;
private dropDownElement: HTMLElement;
private borderColor: Color;
private disposables: IDisposable[] = [];
constructor(parent: HTMLElement, private _uri: URI, private _configuartionTarget: 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
}));
constructor(
action: IAction,
@IWorkspaceContextService private contextService: IWorkspaceContextService,
@IContextMenuService private contextMenuService: IContextMenuService
) {
super(null, action);
const workspace = this.contextService.getWorkspace();
this._folder = workspace.folders.length === 1 ? workspace.folders[0] : null;
this.disposables.push(this.contextService.onDidChangeWorkspaceFolders(() => this.onWorkspaceFoldersChanged()));
}
get configurationTarget(): ConfigurationTarget {
return this._configuartionTarget;
get folder(): IWorkspaceFolder {
return this._folder;
}
public updateTargets(uri: URI, configuartionTarget: ConfigurationTarget): void {
this._uri = uri;
this._configuartionTarget = configuartionTarget;
this.updateLabel();
set folder(folder: IWorkspaceFolder) {
this._folder = folder;
this.update();
}
private create(parent: HTMLElement): void {
this.settingsTargetsContainer = DOM.append(parent, DOM.$('.settings-targets-widget'));
this.settingsTargetsContainer.style.width = this.workspaceContextService.getWorkbenchState() === WorkbenchState.WORKSPACE ? '200px' : '150px';
public render(container: HTMLElement): void {
this.builder = $(container);
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.container = container;
this.labelElement = DOM.$('.action-title');
this.detailsElement = DOM.$('.action-details');
this.dropDownElement = DOM.$('.dropdown-icon.octicon.octicon-triangle-down.hide');
this.anchorElement = DOM.$('a.action-label', {
role: 'button',
'aria-haspopup': 'true',
'tabindex': '0'
}, this.labelElement, this.detailsElement, this.dropDownElement);
this.disposables.push(DOM.addDisposableListener(this.anchorElement, DOM.EventType.CLICK, e => this.onClick(e)));
this.disposables.push(DOM.addDisposableListener(this.anchorElement, DOM.EventType.KEY_UP, e => this.onKeyUp(e)));
this.onclick(this.settingsTargetsContainer, e => this.showContextMenu(e));
DOM.append(this.container, this.anchorElement);
DOM.append(this.settingsTargetsContainer, DOM.$('.settings-target-dropdown-icon.octicon.octicon-triangle-down'));
this.applyStyles();
this.update();
}
private updateLabel(): void {
this.targetLabel.textContent = getSettingsTargetName(this._configuartionTarget, this._uri, this.workspaceContextService);
const details = ConfigurationTarget.WORKSPACE_FOLDER === this._configuartionTarget ? 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.toString() === userSettingsResource.toString(),
enabled: true,
run: () => this.onTargetClicked(userSettingsResource)
});
if (this.workspaceContextService.getWorkbenchState() !== WorkbenchState.EMPTY) {
const workspaceSettingsResource = this.preferencesService.workspaceSettingsResource;
actions.push(<IAction>{
id: 'workspaceSettingsTarget',
label: getSettingsTargetName(ConfigurationTarget.WORKSPACE, workspaceSettingsResource, this.workspaceContextService),
checked: this._uri.toString() === workspaceSettingsResource.toString(),
enabled: true,
run: () => this.onTargetClicked(workspaceSettingsResource)
});
private onKeyUp(event: any): void {
const keyboardEvent = new StandardKeyboardEvent(event);
switch (keyboardEvent.keyCode) {
case KeyCode.Enter:
case KeyCode.Space:
this.onClick(event);
return;
}
}
const workspaceFolders = this.workspaceContextService.getWorkspace().folders;
if (this.workspaceContextService.getWorkbenchState() === WorkbenchState.WORKSPACE && workspaceFolders.length > 0) {
public onClick(event: DOM.EventLike): void {
DOM.EventHelper.stop(event, true);
if (!this.folder || this._action.checked) {
this.showMenu();
} else {
this._action.run(this._folder);
}
}
protected _updateEnabled(): void {
this.update();
}
protected _updateChecked(): void {
this.update();
}
private onWorkspaceFoldersChanged(): void {
const oldFolder = this._folder;
const workspace = this.contextService.getWorkspace();
if (this._folder) {
this._folder = workspace.folders.filter(folder => folder.uri.toString() === this._folder.uri.toString())[0] || workspace.folders[0];
}
this._folder = this._folder ? this._folder : workspace.folders.length === 1 ? workspace.folders[0] : null;
this.update();
if (this._action.checked) {
if ((oldFolder || !this._folder)
|| (!oldFolder || this._folder)
|| (oldFolder && this._folder && oldFolder.uri.toString() === this._folder.uri.toString())) {
this._action.run(this._folder);
}
}
}
private update(): void {
const workspace = this.contextService.getWorkspace();
if (this._folder) {
this.labelElement.textContent = this._folder.name;
this.anchorElement.title = this._folder.name;
this.detailsElement.textContent = this._action.label;
DOM.toggleClass(this.dropDownElement, 'hide', workspace.folders.length === 1 || !this._action.checked);
} else {
this.labelElement.textContent = this._action.label;
this.detailsElement.textContent = '';
this.anchorElement.title = this._action.label;
DOM.removeClass(this.dropDownElement, 'hide');
}
DOM.toggleClass(this.anchorElement, 'checked', this._action.checked);
DOM.toggleClass(this.container, 'disabled', !this._action.enabled);
}
private showMenu(): void {
this.contextMenuService.showContextMenu({
getAnchor: () => this.container,
getActions: () => TPromise.as(this.getDropdownMenuActions()),
getActionItem: (action) => null,
onHide: () => {
this.anchorElement.blur();
}
});
}
private getDropdownMenuActions(): IAction[] {
const actions: IAction[] = [];
const workspaceFolders = this.contextService.getWorkspace().folders;
if (this.contextService.getWorkbenchState() === WorkbenchState.WORKSPACE && workspaceFolders.length > 0) {
actions.push(new Separator());
actions.push(...workspaceFolders.map((folder, index) => {
return <IAction>{
id: 'folderSettingsTarget' + index,
label: getSettingsTargetName(ConfigurationTarget.WORKSPACE_FOLDER, folder.uri, this.workspaceContextService),
checked: this._uri.toString() === folder.uri.toString(),
label: folder.name,
checked: this.folder && this.folder.uri.toString() === folder.uri.toString(),
enabled: true,
run: () => this.onTargetClicked(folder.uri)
run: () => this._action.run(folder)
};
}));
}
return actions;
}
private onTargetClicked(target: URI): void {
if (this._uri.toString() === target.toString()) {
return;
}
this._onDidTargetChange.fire(target);
public dispose(): void {
dispose(this.disposables);
super.dispose();
}
}
export type SettingsTarget = ConfigurationTarget.USER | ConfigurationTarget.WORKSPACE | URI;
export class SettingsTargetsWidget extends Widget {
private settingsSwitcherBar: ActionBar;
private userSettings: Action;
private workspaceSettings: Action;
private folderSettings: FolderSettingsActionItem;
private _settingsTarget: SettingsTarget;
private _onDidTargetChange: Emitter<SettingsTarget> = new Emitter<SettingsTarget>();
public readonly onDidTargetChange: Event<SettingsTarget> = this._onDidTargetChange.event;
constructor(
parent: HTMLElement,
@IWorkspaceContextService private contextService: IWorkspaceContextService,
@IInstantiationService private instantiationService: IInstantiationService
) {
super();
this.create(parent);
this._register(this.contextService.onDidChangeWorkbenchState(() => this.onWorkbenchStateChanged()));
this._register(this.contextService.onDidChangeWorkspaceFolders(() => this.update()));
}
style(styles: ISelectBoxStyles): void {
this.borderColor = styles.selectBorder;
this.applyStyles();
private create(parent: HTMLElement): void {
const settingsTabsWidget = DOM.append(parent, DOM.$('.settings-tabs-widget'));
this.settingsSwitcherBar = this._register(new ActionBar(settingsTabsWidget, {
orientation: ActionsOrientation.HORIZONTAL,
ariaLabel: localize('settingsSwitcherBarAriaLabel', "Settings Switcher"),
animated: false,
actionItemProvider: (action: Action) => action.id === 'folderSettings' ? this.folderSettings : null
}));
this.userSettings = new Action('userSettings', localize('userSettings', "User Settings"), '.settings-tab', true, () => this.updateTarget(ConfigurationTarget.USER));
this.userSettings.tooltip = this.userSettings.label;
this.workspaceSettings = new Action('workspaceSettings', localize('workspaceSettings', "Workspace Settings"), '.settings-tab', false, () => this.updateTarget(ConfigurationTarget.WORKSPACE));
this.workspaceSettings.tooltip = this.workspaceSettings.label;
const folderSettingsAction = new Action('folderSettings', localize('folderSettings', "Folder Settings"), '.settings-tab', false, (folder: IWorkspaceFolder) => this.updateTarget(folder ? folder.uri : ConfigurationTarget.USER));
this.folderSettings = this.instantiationService.createInstance(FolderSettingsActionItem, folderSettingsAction);
this.update();
this.settingsSwitcherBar.push([this.userSettings, this.workspaceSettings, folderSettingsAction]);
}
private applyStyles(): void {
if (this.settingsTargetsContainer) {
this.settingsTargetsContainer.style.border = this.borderColor ? `1px solid ${this.borderColor}` : null;
public get settingsTarget(): SettingsTarget {
return this._settingsTarget;
}
public set settingsTarget(settingsTarget: SettingsTarget) {
this._settingsTarget = settingsTarget;
this.userSettings.checked = ConfigurationTarget.USER === this.settingsTarget;
this.workspaceSettings.checked = ConfigurationTarget.WORKSPACE === this.settingsTarget;
if (this.settingsTarget instanceof URI) {
this.folderSettings.getAction().checked = true;
this.folderSettings.folder = this.contextService.getWorkspaceFolder(this.settingsTarget as URI);
} else {
this.folderSettings.getAction().checked = false;
}
}
private onWorkbenchStateChanged(): void {
this.folderSettings.folder = null;
this.update();
if (this.settingsTarget === ConfigurationTarget.WORKSPACE && this.contextService.getWorkbenchState() === WorkbenchState.WORKSPACE) {
this.updateTarget(ConfigurationTarget.USER);
}
}
private updateTarget(settingsTarget: SettingsTarget): TPromise<void> {
const isSameTarget = this.settingsTarget === settingsTarget || settingsTarget instanceof URI && this.settingsTarget instanceof URI && this.settingsTarget.toString() === settingsTarget.toString();
if (!isSameTarget) {
this.settingsTarget = settingsTarget;
this._onDidTargetChange.fire(this.settingsTarget);
}
return TPromise.as(null);
}
private update(): void {
DOM.toggleClass(this.settingsSwitcherBar.domNode, 'empty-workbench', this.contextService.getWorkbenchState() === WorkbenchState.EMPTY);
this.workspaceSettings.enabled = this.contextService.getWorkbenchState() !== WorkbenchState.EMPTY;
this.folderSettings.getAction().enabled = this.contextService.getWorkbenchState() === WorkbenchState.WORKSPACE && this.contextService.getWorkspace().folders.length > 0;
}
}
export interface SearchOptions extends IInputOptions {
focusKey?: IContextKey<boolean>;
showFuzzyToggle?: boolean;
showResultCount?: boolean;
}
export class SearchWidget extends Widget {
@@ -444,7 +555,6 @@ export class SearchWidget extends Widget {
constructor(parent: HTMLElement, protected options: SearchOptions,
@IContextViewService private contextViewService: IContextViewService,
@IContextMenuService private contextMenuService: IContextMenuService,
@IInstantiationService protected instantiationService: IInstantiationService,
@IThemeService private themeService: IThemeService
) {
@@ -464,39 +574,44 @@ export class SearchWidget extends Widget {
this.domNode = DOM.append(parent, DOM.$('div.settings-header-widget'));
this.createSearchContainer(DOM.append(this.domNode, DOM.$('div.settings-search-container')));
this.controlsDiv = DOM.append(this.domNode, DOM.$('div.settings-search-controls'));
this.fuzzyToggle = this._register(new Checkbox({
actionClassName: 'prefs-fuzzy-search-toggle',
isChecked: false,
onChange: () => {
this.inputBox.focus();
this._onDidChange.fire();
},
title: localize('enableFuzzySearch', 'Enable experimental fuzzy search')
}));
DOM.append(this.controlsDiv, this.fuzzyToggle.domNode);
this._register(attachCheckboxStyler(this.fuzzyToggle, this.themeService));
if (this.options.showFuzzyToggle) {
this.fuzzyToggle = this._register(new Checkbox({
actionClassName: 'prefs-natural-language-search-toggle',
isChecked: false,
onChange: () => {
this.inputBox.focus();
this._onDidChange.fire();
},
title: localize('enableFuzzySearch', 'Enable natural language search')
}));
this.fuzzyToggle.domNode.innerHTML = renderOcticons('$(light-bulb)');
DOM.append(this.controlsDiv, this.fuzzyToggle.domNode);
this._register(attachCheckboxStyler(this.fuzzyToggle, this.themeService));
}
this.countElement = DOM.append(this.controlsDiv, DOM.$('.settings-count-widget'));
this._register(attachStylerCallback(this.themeService, { badgeBackground, contrastBorder }, colors => {
const background = colors.badgeBackground ? colors.badgeBackground.toString() : null;
const border = colors.contrastBorder ? colors.contrastBorder.toString() : null;
if (this.options.showResultCount) {
this.countElement = DOM.append(this.controlsDiv, DOM.$('.settings-count-widget'));
this._register(attachStylerCallback(this.themeService, { badgeBackground, contrastBorder }, colors => {
const background = colors.badgeBackground ? colors.badgeBackground.toString() : null;
const border = colors.contrastBorder ? colors.contrastBorder.toString() : null;
this.countElement.style.backgroundColor = background;
this.countElement.style.backgroundColor = background;
this.countElement.style.borderWidth = border ? '1px' : null;
this.countElement.style.borderStyle = border ? 'solid' : null;
this.countElement.style.borderColor = border;
this.countElement.style.borderWidth = border ? '1px' : null;
this.countElement.style.borderStyle = border ? 'solid' : null;
this.countElement.style.borderColor = border;
this.styleCountElementForeground();
}));
}
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()));
this._register(focusTracker.onDidFocus(() => 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)));
this._register(focusTracker.onDidFocus(() => this.options.focusKey.set(true)));
this._register(focusTracker.onDidBlur(() => this.options.focusKey.set(false)));
}
}
@@ -515,11 +630,13 @@ export class SearchWidget extends Widget {
}
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 = this.getControlsWidth() + 'px';
this.styleCountElementForeground();
if (this.countElement) {
this.countElement.textContent = message;
this.inputBox.inputElement.setAttribute('aria-label', message);
DOM.toggleClass(this.countElement, 'no-results', count === 0);
this.inputBox.inputElement.style.paddingRight = this.getControlsWidth() + 'px';
this.styleCountElementForeground();
}
}
public setFuzzyToggleVisible(visible: boolean): void {
@@ -540,16 +657,24 @@ export class SearchWidget extends Widget {
public layout(dimension: Dimension) {
if (dimension.width < 400) {
DOM.addClass(this.countElement, 'hide');
if (this.countElement) {
DOM.addClass(this.countElement, 'hide');
}
this.inputBox.inputElement.style.paddingRight = '0px';
} else {
DOM.removeClass(this.countElement, 'hide');
if (this.countElement) {
DOM.removeClass(this.countElement, 'hide');
}
this.inputBox.inputElement.style.paddingRight = this.getControlsWidth() + 'px';
}
}
private getControlsWidth(): number {
return DOM.getTotalWidth(this.countElement) + DOM.getTotalWidth(this.fuzzyToggle.domNode) + 20;
const countWidth = this.countElement ? DOM.getTotalWidth(this.countElement) : 0;
const fuzzyToggleWidth = this.fuzzyToggle ? DOM.getTotalWidth(this.fuzzyToggle.domNode) : 0;
return countWidth + fuzzyToggleWidth + 20;
}
public focus() {
@@ -593,7 +718,7 @@ export class FloatingClickWidget extends Widget implements IOverlayWidget {
constructor(
private editor: ICodeEditor,
private label: string,
private keyBindingAction: string,
keyBindingAction: string,
@IKeybindingService keybindingService: IKeybindingService,
@IThemeService private themeService: IThemeService
) {
@@ -641,7 +766,7 @@ export class FloatingClickWidget extends Widget implements IOverlayWidget {
export class EditPreferenceWidget<T> extends Disposable {
public static GLYPH_MARGIN_CLASS_NAME = 'edit-preferences-widget';
public static readonly GLYPH_MARGIN_CLASS_NAME = 'edit-preferences-widget';
private _line: number;
private _preferences: T[];
@@ -705,3 +830,72 @@ export class EditPreferenceWidget<T> extends Disposable {
super.dispose();
}
}
registerThemingParticipant((theme: ITheme, collector: ICssStyleCollector) => {
collector.addRule(`
.settings-tabs-widget > .monaco-action-bar .action-item .action-label:focus,
.settings-tabs-widget > .monaco-action-bar .action-item .action-label.checked {
border-bottom: 1px solid;
}
`);
// Title Active
const titleActive = theme.getColor(PANEL_ACTIVE_TITLE_FOREGROUND);
const titleActiveBorder = theme.getColor(PANEL_ACTIVE_TITLE_BORDER);
if (titleActive || titleActiveBorder) {
collector.addRule(`
.settings-tabs-widget > .monaco-action-bar .action-item .action-label:hover,
.settings-tabs-widget > .monaco-action-bar .action-item .action-label.checked {
color: ${titleActive};
border-bottom-color: ${titleActiveBorder};
}
`);
}
// Title Inactive
const titleInactive = theme.getColor(PANEL_INACTIVE_TITLE_FOREGROUND);
if (titleInactive) {
collector.addRule(`
.settings-tabs-widget > .monaco-action-bar .action-item .action-label {
color: ${titleInactive};
}
`);
}
// Title focus
const focusBorderColor = theme.getColor(focusBorder);
if (focusBorderColor) {
collector.addRule(`
.settings-tabs-widget > .monaco-action-bar .action-item .action-label:focus {
border-bottom-color: ${focusBorderColor} !important;
}
`);
collector.addRule(`
.settings-tabs-widget > .monaco-action-bar .action-item .action-label:focus {
outline: none;
}
`);
}
// Styling with Outline color (e.g. high contrast theme)
const outline = theme.getColor(activeContrastBorder);
if (outline) {
const outline = theme.getColor(activeContrastBorder);
collector.addRule(`
.settings-tabs-widget > .monaco-action-bar .action-item .action-label.checked,
.settings-tabs-widget > .monaco-action-bar .action-item .action-label:hover {
outline-color: ${outline};
outline-width: 1px;
outline-style: solid;
border-bottom: none;
padding-bottom: 0;
outline-offset: 3px;
}
.settings-tabs-widget > .monaco-action-bar .action-item .action-label:not(.checked):hover {
outline-style: dashed;
}
`);
}
});