mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-03-30 08:40:29 -04:00
Merge VS Code 1.21 source code (#1067)
* Initial VS Code 1.21 file copy with patches * A few more merges * Post npm install * Fix batch of build breaks * Fix more build breaks * Fix more build errors * Fix more build breaks * Runtime fixes 1 * Get connection dialog working with some todos * Fix a few packaging issues * Copy several node_modules to package build to fix loader issues * Fix breaks from master * A few more fixes * Make tests pass * First pass of license header updates * Second pass of license header updates * Fix restore dialog issues * Remove add additional themes menu items * fix select box issues where the list doesn't show up * formatting * Fix editor dispose issue * Copy over node modules to correct location on all platforms
This commit is contained in:
@@ -9,6 +9,7 @@ import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import { Delayer } from 'vs/base/common/async';
|
||||
import * as DOM from 'vs/base/browser/dom';
|
||||
import { OS } from 'vs/base/common/platform';
|
||||
import { dispose } from 'vs/base/common/lifecycle';
|
||||
import { Checkbox } from 'vs/base/browser/ui/checkbox/checkbox';
|
||||
import { Builder, Dimension } from 'vs/base/browser/builder';
|
||||
import { HighlightedLabel } from 'vs/base/browser/ui/highlightedlabel/highlightedLabel';
|
||||
@@ -26,7 +27,7 @@ import { SearchWidget } from 'vs/workbench/parts/preferences/browser/preferences
|
||||
import { DefineKeybindingWidget } from 'vs/workbench/parts/preferences/browser/keybindingWidgets';
|
||||
import {
|
||||
IPreferencesService, IKeybindingsEditor, CONTEXT_KEYBINDING_FOCUS, CONTEXT_KEYBINDINGS_EDITOR, CONTEXT_KEYBINDINGS_SEARCH_FOCUS, KEYBINDINGS_EDITOR_COMMAND_REMOVE, KEYBINDINGS_EDITOR_COMMAND_COPY,
|
||||
KEYBINDINGS_EDITOR_COMMAND_RESET, KEYBINDINGS_EDITOR_COMMAND_DEFINE, KEYBINDINGS_EDITOR_COMMAND_SHOW_CONFLICTS
|
||||
KEYBINDINGS_EDITOR_COMMAND_RESET, KEYBINDINGS_EDITOR_COMMAND_COPY_COMMAND, KEYBINDINGS_EDITOR_COMMAND_DEFINE, KEYBINDINGS_EDITOR_COMMAND_SHOW_SIMILAR
|
||||
} from 'vs/workbench/parts/preferences/common/preferences';
|
||||
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
|
||||
import { IKeybindingEditingService } from 'vs/workbench/services/keybinding/common/keybindingEditing';
|
||||
@@ -34,19 +35,19 @@ 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 { IMessageService, Severity } from 'vs/platform/message/common/message';
|
||||
import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent';
|
||||
import { KeyCode, ResolvedKeybinding } from 'vs/base/common/keyCodes';
|
||||
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';
|
||||
import { WorkbenchList } from 'vs/platform/list/browser/listService';
|
||||
import { INotificationService } from 'vs/platform/notification/common/notification';
|
||||
|
||||
let $ = DOM.$;
|
||||
|
||||
export class KeybindingsEditorInput extends EditorInput {
|
||||
|
||||
public static ID: string = 'workbench.input.keybindings';
|
||||
public static readonly ID: string = 'workbench.input.keybindings';
|
||||
public readonly keybindingsModel: KeybindingsEditorModel;
|
||||
|
||||
constructor( @IInstantiationService instantiationService: IInstantiationService) {
|
||||
@@ -73,7 +74,7 @@ export class KeybindingsEditorInput extends EditorInput {
|
||||
|
||||
export class KeybindingsEditor extends BaseEditor implements IKeybindingsEditor {
|
||||
|
||||
public static ID: string = 'workbench.editor.keybindings';
|
||||
public static readonly ID: string = 'workbench.editor.keybindings';
|
||||
|
||||
private keybindingsEditorModel: KeybindingsEditorModel;
|
||||
|
||||
@@ -104,9 +105,8 @@ export class KeybindingsEditor extends BaseEditor implements IKeybindingsEditor
|
||||
@IContextMenuService private contextMenuService: IContextMenuService,
|
||||
@IPreferencesService private preferencesService: IPreferencesService,
|
||||
@IKeybindingEditingService private keybindingEditingService: IKeybindingEditingService,
|
||||
@IListService private listService: IListService,
|
||||
@IContextKeyService private contextKeyService: IContextKeyService,
|
||||
@IMessageService private messageService: IMessageService,
|
||||
@INotificationService private notificationService: INotificationService,
|
||||
@IClipboardService private clipboardService: IClipboardService,
|
||||
@IInstantiationService private instantiationService: IInstantiationService,
|
||||
@IWorkbenchEditorService private editorService: IWorkbenchEditorService
|
||||
@@ -180,14 +180,17 @@ export class KeybindingsEditor extends BaseEditor implements IKeybindingsEditor
|
||||
this.selectEntry(keybindingEntry);
|
||||
this.showOverlayContainer();
|
||||
return this.defineKeybindingWidget.define().then(key => {
|
||||
this.reportKeybindingAction(KEYBINDINGS_EDITOR_COMMAND_DEFINE, keybindingEntry.keybindingItem.command, key);
|
||||
if (key) {
|
||||
return this.keybindingEditingService.editKeybinding(key, keybindingEntry.keybindingItem.keybindingItem)
|
||||
.then(() => {
|
||||
if (!keybindingEntry.keybindingItem.keybinding) { // reveal only if keybinding was added to unassinged. Because the entry will be placed in different position after rendering
|
||||
this.unAssignedKeybindingItemToRevealAndFocus = keybindingEntry;
|
||||
}
|
||||
});
|
||||
const currentKey = keybindingEntry.keybindingItem.keybinding ? keybindingEntry.keybindingItem.keybinding.getUserSettingsLabel() : '';
|
||||
if (currentKey !== key) {
|
||||
this.reportKeybindingAction(KEYBINDINGS_EDITOR_COMMAND_DEFINE, keybindingEntry.keybindingItem.command, key);
|
||||
return this.keybindingEditingService.editKeybinding(key, keybindingEntry.keybindingItem.keybindingItem)
|
||||
.then(() => {
|
||||
if (!keybindingEntry.keybindingItem.keybinding) { // reveal only if keybinding was added to unassinged. Because the entry will be placed in different position after rendering
|
||||
this.unAssignedKeybindingItemToRevealAndFocus = keybindingEntry;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}).then(() => {
|
||||
@@ -245,6 +248,13 @@ export class KeybindingsEditor extends BaseEditor implements IKeybindingsEditor
|
||||
return TPromise.as(null);
|
||||
}
|
||||
|
||||
copyKeybindingCommand(keybinding: IKeybindingItemEntry): TPromise<any> {
|
||||
this.selectEntry(keybinding);
|
||||
this.reportKeybindingAction(KEYBINDINGS_EDITOR_COMMAND_COPY_COMMAND, keybinding.keybindingItem.command, keybinding.keybindingItem.keybinding);
|
||||
this.clipboardService.writeText(keybinding.keybindingItem.command);
|
||||
return TPromise.as(null);
|
||||
}
|
||||
|
||||
search(filter: string): void {
|
||||
this.searchWidget.focus();
|
||||
}
|
||||
@@ -253,7 +263,7 @@ export class KeybindingsEditor extends BaseEditor implements IKeybindingsEditor
|
||||
this.searchWidget.clear();
|
||||
}
|
||||
|
||||
showConflicts(keybindingEntry: IKeybindingItemEntry): TPromise<any> {
|
||||
showSimilarKeybindings(keybindingEntry: IKeybindingItemEntry): TPromise<any> {
|
||||
const value = `"${keybindingEntry.keybindingItem.keybinding.getAriaLabel()}"`;
|
||||
if (value !== this.searchWidget.getValue()) {
|
||||
this.searchWidget.setValue(value);
|
||||
@@ -326,8 +336,8 @@ 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 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.keybindingsList = this._register(this.instantiationService.createInstance(WorkbenchList, this.keybindingsListContainer, new Delegate(), [new KeybindingHeaderRenderer(), new KeybindingItemRenderer(this, this.keybindingsService)],
|
||||
{ identityProvider: e => e.id, mouseSupport: true, ariaLabel: localize('keybindingsLabel', "Keybindings") })) as WorkbenchList<IListEntry>;
|
||||
this._register(this.keybindingsList.onContextMenu(e => this.onContextMenu(e)));
|
||||
this._register(this.keybindingsList.onFocusChange(e => this.onFocusChange(e)));
|
||||
this._register(this.keybindingsList.onDidFocus(() => {
|
||||
@@ -337,7 +347,8 @@ export class KeybindingsEditor extends BaseEditor implements IKeybindingsEditor
|
||||
DOM.removeClass(this.keybindingsList.getHTMLElement(), 'focused');
|
||||
this.keybindingFocusContextKey.reset();
|
||||
}));
|
||||
this._register(this.keybindingsList.onKeyUp(e => {
|
||||
this._register(this.keybindingsList.onMouseDblClick(() => this.defineKeybinding(this.activeKeybindingEntry)));
|
||||
this._register(this.keybindingsList.onKeyDown(e => {
|
||||
const event = new StandardKeyboardEvent(e);
|
||||
if (event.keyCode === KeyCode.Enter) {
|
||||
const keybindingEntry = this.activeKeybindingEntry;
|
||||
@@ -455,6 +466,7 @@ export class KeybindingsEditor extends BaseEditor implements IKeybindingsEditor
|
||||
getAnchor: () => e.anchor,
|
||||
getActions: () => TPromise.as([
|
||||
this.createCopyAction(<IKeybindingItemEntry>e.element),
|
||||
this.createCopyCommandAction(<IKeybindingItemEntry>e.element),
|
||||
new Separator(),
|
||||
this.createDefineAction(<IKeybindingItemEntry>e.element),
|
||||
this.createRemoveAction(<IKeybindingItemEntry>e.element),
|
||||
@@ -509,10 +521,10 @@ export class KeybindingsEditor extends BaseEditor implements IKeybindingsEditor
|
||||
|
||||
private createShowConflictsAction(keybindingItem: IKeybindingItemEntry): IAction {
|
||||
return <IAction>{
|
||||
label: localize('showConflictsLabel', "Show Conflicts"),
|
||||
label: localize('showSameKeybindings', "Show Same Keybindings"),
|
||||
enabled: !!keybindingItem.keybindingItem.keybinding,
|
||||
id: KEYBINDINGS_EDITOR_COMMAND_SHOW_CONFLICTS,
|
||||
run: () => this.showConflicts(keybindingItem)
|
||||
id: KEYBINDINGS_EDITOR_COMMAND_SHOW_SIMILAR,
|
||||
run: () => this.showSimilarKeybindings(keybindingItem)
|
||||
};
|
||||
}
|
||||
|
||||
@@ -525,6 +537,15 @@ export class KeybindingsEditor extends BaseEditor implements IKeybindingsEditor
|
||||
};
|
||||
}
|
||||
|
||||
private createCopyCommandAction(keybinding: IKeybindingItemEntry): IAction {
|
||||
return <IAction>{
|
||||
label: localize('copyCommandLabel', "Copy Command"),
|
||||
enabled: true,
|
||||
id: KEYBINDINGS_EDITOR_COMMAND_COPY_COMMAND,
|
||||
run: () => this.copyKeybindingCommand(keybinding)
|
||||
};
|
||||
}
|
||||
|
||||
private reportFilteringUsed(filter: string): void {
|
||||
if (filter) {
|
||||
let data = {
|
||||
@@ -557,7 +578,7 @@ export class KeybindingsEditor extends BaseEditor implements IKeybindingsEditor
|
||||
}
|
||||
|
||||
private onKeybindingEditingError(error: any): void {
|
||||
this.messageService.show(Severity.Error, typeof error === 'string' ? error : localize('error', "Error '{0}' while editing keybinding. Please open 'keybindings.json' file and check.", `${error}`));
|
||||
this.notificationService.error(typeof error === 'string' ? error : localize('error', "Error '{0}' while editing the keybinding. Please open 'keybindings.json' file and check for errors.", `${error}`));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -643,7 +664,7 @@ class KeybindingItemRenderer implements IRenderer<IKeybindingItemEntry, Keybindi
|
||||
}
|
||||
|
||||
renderElement(keybindingEntry: IKeybindingItemEntry, index: number, template: KeybindingItemTemplate): void {
|
||||
DOM.toggleClass(template.parent, 'even', index % 2 === 0);
|
||||
DOM.toggleClass(template.parent, 'odd', index % 2 === 1);
|
||||
template.actions.render(keybindingEntry);
|
||||
template.command.render(keybindingEntry);
|
||||
template.keybinding.render(keybindingEntry);
|
||||
@@ -652,6 +673,7 @@ class KeybindingItemRenderer implements IRenderer<IKeybindingItemEntry, Keybindi
|
||||
}
|
||||
|
||||
disposeTemplate(template: KeybindingItemTemplate): void {
|
||||
template.actions.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -716,6 +738,10 @@ class ActionsColumn extends Column {
|
||||
run: () => this.keybindingsEditor.defineKeybinding(keybindingItemEntry)
|
||||
};
|
||||
}
|
||||
|
||||
public dispose(): void {
|
||||
this.actionBar = dispose(this.actionBar);
|
||||
}
|
||||
}
|
||||
|
||||
class CommandColumn extends Column {
|
||||
|
||||
@@ -28,6 +28,7 @@ import { EditorContextKeys } from 'vs/editor/common/editorContextKeys';
|
||||
import { WindowsNativeResolvedKeybinding } from 'vs/workbench/services/keybinding/common/windowsKeyboardMapper';
|
||||
import { themeColorFromId, ThemeColor } from 'vs/platform/theme/common/themeService';
|
||||
import { overviewRulerInfo, overviewRulerError } from 'vs/editor/common/view/editorColorRegistry';
|
||||
import { IModelDeltaDecoration, ITextModel, TrackedRangeStickiness, OverviewRulerLane } from 'vs/editor/common/model';
|
||||
|
||||
const NLS_LAUNCH_MESSAGE = nls.localize('defineKeybinding.start', "Define Keybinding");
|
||||
const NLS_KB_LAYOUT_ERROR_MESSAGE = nls.localize('defineKeybinding.kbLayoutErrorMessage', "You won't be able to produce this key combination under your current keyboard layout.");
|
||||
@@ -47,7 +48,7 @@ export class DefineKeybindingController extends Disposable implements editorComm
|
||||
|
||||
constructor(
|
||||
private _editor: ICodeEditor,
|
||||
@IInstantiationService private _instantiationService: IInstantiationService
|
||||
@IInstantiationService private readonly _instantiationService: IInstantiationService
|
||||
) {
|
||||
super();
|
||||
|
||||
@@ -124,7 +125,7 @@ export class KeybindingWidgetRenderer extends Disposable {
|
||||
|
||||
constructor(
|
||||
private _editor: ICodeEditor,
|
||||
@IInstantiationService private _instantiationService: IInstantiationService
|
||||
@IInstantiationService private readonly _instantiationService: IInstantiationService
|
||||
) {
|
||||
super();
|
||||
this._launchWidget = this._register(this._instantiationService.createInstance(FloatingClickWidget, this._editor, NLS_LAUNCH_MESSAGE, DefineKeybindingCommand.ID));
|
||||
@@ -170,7 +171,7 @@ export class KeybindingEditorDecorationsRenderer extends Disposable {
|
||||
|
||||
constructor(
|
||||
private _editor: ICodeEditor,
|
||||
@IKeybindingService private _keybindingService: IKeybindingService,
|
||||
@IKeybindingService private readonly _keybindingService: IKeybindingService,
|
||||
) {
|
||||
super();
|
||||
|
||||
@@ -191,7 +192,7 @@ export class KeybindingEditorDecorationsRenderer extends Disposable {
|
||||
private _updateDecorationsNow(): void {
|
||||
const model = this._editor.getModel();
|
||||
|
||||
let newDecorations: editorCommon.IModelDeltaDecoration[] = [];
|
||||
let newDecorations: IModelDeltaDecoration[] = [];
|
||||
|
||||
const root = parseTree(model.getValue());
|
||||
if (root && Array.isArray(root.children)) {
|
||||
@@ -207,7 +208,7 @@ export class KeybindingEditorDecorationsRenderer extends Disposable {
|
||||
this._dec = this._editor.deltaDecorations(this._dec, newDecorations);
|
||||
}
|
||||
|
||||
private _getDecorationForEntry(model: editorCommon.IModel, entry: Node): editorCommon.IModelDeltaDecoration {
|
||||
private _getDecorationForEntry(model: ITextModel, entry: Node): IModelDeltaDecoration {
|
||||
if (!Array.isArray(entry.children)) {
|
||||
return null;
|
||||
}
|
||||
@@ -288,7 +289,7 @@ export class KeybindingEditorDecorationsRenderer extends Disposable {
|
||||
return false;
|
||||
}
|
||||
|
||||
private _createDecoration(isError: boolean, uiLabel: string, usLabel: string, model: editorCommon.IModel, keyNode: Node): editorCommon.IModelDeltaDecoration {
|
||||
private _createDecoration(isError: boolean, uiLabel: string, usLabel: string, model: ITextModel, keyNode: Node): IModelDeltaDecoration {
|
||||
let msg: MarkdownString;
|
||||
let className: string;
|
||||
let beforeContentClassName: string;
|
||||
@@ -339,14 +340,14 @@ export class KeybindingEditorDecorationsRenderer extends Disposable {
|
||||
return {
|
||||
range: range,
|
||||
options: {
|
||||
stickiness: editorCommon.TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges,
|
||||
stickiness: TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges,
|
||||
className: className,
|
||||
beforeContentClassName: beforeContentClassName,
|
||||
hoverMessage: msg,
|
||||
overviewRuler: {
|
||||
color: overviewRulerColor,
|
||||
darkColor: overviewRulerColor,
|
||||
position: editorCommon.OverviewRulerLane.Right
|
||||
position: OverviewRulerLane.Right
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -356,12 +357,12 @@ export class KeybindingEditorDecorationsRenderer extends Disposable {
|
||||
|
||||
class DefineKeybindingCommand extends EditorCommand {
|
||||
|
||||
static ID = 'editor.action.defineKeybinding';
|
||||
static readonly ID = 'editor.action.defineKeybinding';
|
||||
|
||||
constructor() {
|
||||
super({
|
||||
id: DefineKeybindingCommand.ID,
|
||||
precondition: ContextKeyExpr.and(EditorContextKeys.writable, EditorContextKeys.languageId.isEqualTo('json')),
|
||||
precondition: ContextKeyExpr.and(EditorContextKeys.writable, EditorContextKeys.languageId.isEqualTo('jsonc')),
|
||||
kbOpts: {
|
||||
kbExpr: EditorContextKeys.textFocus,
|
||||
primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.KEY_K)
|
||||
|
||||
@@ -68,9 +68,9 @@
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.keybindings-editor > .keybindings-body > .keybindings-list-container .monaco-list-row.even:not(.focused):not(.selected):not(:hover),
|
||||
.keybindings-editor > .keybindings-body > .keybindings-list-container .monaco-list:not(:focus) .monaco-list-row.focused.even:not(.selected):not(:hover),
|
||||
.keybindings-editor > .keybindings-body > .keybindings-list-container .monaco-list:not(.focused) .monaco-list-row.focused.even:not(.selected):not(:hover) {
|
||||
.keybindings-editor > .keybindings-body > .keybindings-list-container .monaco-list-row.odd:not(.focused):not(.selected):not(:hover),
|
||||
.keybindings-editor > .keybindings-body > .keybindings-list-container .monaco-list:not(:focus) .monaco-list-row.focused.odd:not(.selected):not(:hover),
|
||||
.keybindings-editor > .keybindings-body > .keybindings-list-container .monaco-list:not(.focused) .monaco-list-row.focused.odd:not(.selected):not(:hover) {
|
||||
background-color: rgba(130, 130, 130, 0.04);
|
||||
}
|
||||
|
||||
|
||||
@@ -48,6 +48,10 @@
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.settings-tabs-widget > .monaco-action-bar .action-item .action-label:not([aria-haspopup]) {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.settings-tabs-widget > .monaco-action-bar .actions-container {
|
||||
justify-content: flex-start;
|
||||
}
|
||||
@@ -309,4 +313,4 @@
|
||||
.vs-dark .title-actions .action-item .icon.collapseAll,
|
||||
.vs-dark .editor-actions .action-item .icon.collapseAll {
|
||||
background: url('collapseAll_inverse.svg') center center no-repeat;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ import { IQuickOpenService, IPickOpenEntry, IFilePickOpenEntry } from 'vs/platfo
|
||||
import { IPreferencesService } from 'vs/workbench/parts/preferences/common/preferences';
|
||||
import { IWorkspaceContextService, WorkbenchState, IWorkspaceFolder } from 'vs/platform/workspace/common/workspace';
|
||||
import { ICommandService } from 'vs/platform/commands/common/commands';
|
||||
import { PICK_WORKSPACE_FOLDER_COMMAND } from 'vs/workbench/browser/actions/workspaceActions';
|
||||
import { PICK_WORKSPACE_FOLDER_COMMAND_ID } from 'vs/workbench/browser/actions/workspaceCommands';
|
||||
|
||||
export class OpenRawDefaultSettingsAction extends Action {
|
||||
|
||||
@@ -121,10 +121,11 @@ export class OpenWorkspaceSettingsAction extends Action {
|
||||
}
|
||||
|
||||
export const OPEN_FOLDER_SETTINGS_COMMAND = '_workbench.action.openFolderSettings';
|
||||
export const OPEN_FOLDER_SETTINGS_LABEL = nls.localize('openFolderSettings', "Open Folder Settings");
|
||||
export class OpenFolderSettingsAction extends Action {
|
||||
|
||||
public static readonly ID = 'workbench.action.openFolderSettings';
|
||||
public static readonly LABEL = nls.localize('openFolderSettings', "Open Folder Settings");
|
||||
public static readonly LABEL = OPEN_FOLDER_SETTINGS_LABEL;
|
||||
|
||||
private disposables: IDisposable[] = [];
|
||||
|
||||
@@ -146,10 +147,10 @@ export class OpenFolderSettingsAction extends Action {
|
||||
}
|
||||
|
||||
public run(): TPromise<any> {
|
||||
return this.commandService.executeCommand<IWorkspaceFolder>(PICK_WORKSPACE_FOLDER_COMMAND)
|
||||
return this.commandService.executeCommand<IWorkspaceFolder>(PICK_WORKSPACE_FOLDER_COMMAND_ID)
|
||||
.then(workspaceFolder => {
|
||||
if (workspaceFolder) {
|
||||
return this.commandService.executeCommand(OPEN_FOLDER_SETTINGS_COMMAND, workspaceFolder);
|
||||
return this.commandService.executeCommand(OPEN_FOLDER_SETTINGS_COMMAND, workspaceFolder.uri);
|
||||
}
|
||||
return null;
|
||||
});
|
||||
@@ -208,4 +209,4 @@ export class ConfigureLanguageBasedSettingsAction extends Action {
|
||||
});
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,15 +6,16 @@
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import * as nls from 'vs/nls';
|
||||
import URI from 'vs/base/common/uri';
|
||||
import { onUnexpectedError } from 'vs/base/common/errors';
|
||||
import * as strings from 'vs/base/common/strings';
|
||||
import { onUnexpectedError, isPromiseCanceledError, getErrorMessage } from 'vs/base/common/errors';
|
||||
import * as DOM from 'vs/base/browser/dom';
|
||||
import { Delayer, ThrottledDelayer } from 'vs/base/common/async';
|
||||
import * as arrays from 'vs/base/common/arrays';
|
||||
import { Dimension, Builder } from 'vs/base/browser/builder';
|
||||
import { ArrayNavigator, INavigator } from 'vs/base/common/iterator';
|
||||
import { ArrayNavigator } 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 { SideBySideEditorInput, EditorOptions, EditorInput, PREFERENCES_EDITOR_ID } from 'vs/workbench/common/editor';
|
||||
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';
|
||||
@@ -25,7 +26,7 @@ import { CodeEditor } from 'vs/editor/browser/codeEditor';
|
||||
import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
|
||||
import {
|
||||
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
|
||||
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, ISearchProvider, ISearchResult, SETTINGS_EDITOR_COMMAND_EDIT_FOCUSED_SETTING
|
||||
} from 'vs/workbench/parts/preferences/common/preferences';
|
||||
import { SettingsEditorModel, DefaultSettingsEditorModel } from 'vs/workbench/parts/preferences/common/preferencesModels';
|
||||
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
|
||||
@@ -58,9 +59,12 @@ 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';
|
||||
import { IStringDictionary } from 'vs/base/common/collections';
|
||||
import { IProgressService } from 'vs/platform/progress/common/progress';
|
||||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
|
||||
export class PreferencesEditorInput extends SideBySideEditorInput {
|
||||
public static ID: string = 'workbench.editorinputs.preferencesEditorInput';
|
||||
public static readonly ID: string = 'workbench.editorinputs.preferencesEditorInput';
|
||||
|
||||
getTypeId(): string {
|
||||
return PreferencesEditorInput.ID;
|
||||
@@ -101,38 +105,37 @@ export class DefaultPreferencesEditorInput extends ResourceEditorInput {
|
||||
|
||||
export class PreferencesEditor extends BaseEditor {
|
||||
|
||||
public static ID: string = 'workbench.editor.preferencesEditor';
|
||||
public static readonly ID: string = PREFERENCES_EDITOR_ID;
|
||||
|
||||
private defaultSettingsEditorContextKey: IContextKey<boolean>;
|
||||
private focusSettingsContextKey: IContextKey<boolean>;
|
||||
private headerContainer: HTMLElement;
|
||||
private searchWidget: SearchWidget;
|
||||
private sideBySidePreferencesWidget: SideBySidePreferencesWidget;
|
||||
private preferencesRenderers: PreferencesRenderers;
|
||||
private preferencesRenderers: PreferencesRenderersController;
|
||||
|
||||
private delayedFilterLogging: Delayer<void>;
|
||||
private filterThrottle: ThrottledDelayer<void>;
|
||||
private localSearchDelayer: Delayer<void>;
|
||||
private remoteSearchThrottle: ThrottledDelayer<void>;
|
||||
private _lastReportedFilter: string;
|
||||
|
||||
private latestEmptyFilters: string[] = [];
|
||||
private lastFocusedWidget: SearchWidget | SideBySidePreferencesWidget = null;
|
||||
private memento: any;
|
||||
|
||||
constructor(
|
||||
@IPreferencesService private preferencesService: IPreferencesService,
|
||||
@IPreferencesSearchService private preferencesSearchService: IPreferencesSearchService,
|
||||
@ITelemetryService telemetryService: ITelemetryService,
|
||||
@IWorkbenchEditorService private editorService: IWorkbenchEditorService,
|
||||
@IContextKeyService private contextKeyService: IContextKeyService,
|
||||
@IInstantiationService private instantiationService: IInstantiationService,
|
||||
@IThemeService themeService: IThemeService,
|
||||
@IStorageService storageService: IStorageService,
|
||||
@IProgressService private progressService: IProgressService
|
||||
) {
|
||||
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.filterThrottle = new ThrottledDelayer(200);
|
||||
this.memento = this.getMemento(storageService, Scope.WORKSPACE);
|
||||
this.localSearchDelayer = new Delayer(100);
|
||||
this.remoteSearchThrottle = new ThrottledDelayer(200);
|
||||
}
|
||||
|
||||
public createEditor(parent: Builder): void {
|
||||
@@ -145,12 +148,8 @@ export class PreferencesEditor extends BaseEditor {
|
||||
ariaLabel: nls.localize('SearchSettingsWidget.AriaLabel', "Search settings"),
|
||||
placeholder: nls.localize('SearchSettingsWidget.Placeholder', "Search Settings"),
|
||||
focusKey: this.focusSettingsContextKey,
|
||||
showFuzzyToggle: true,
|
||||
showResultCount: true
|
||||
}));
|
||||
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;
|
||||
@@ -160,12 +159,7 @@ export class PreferencesEditor extends BaseEditor {
|
||||
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.preferencesSearchService));
|
||||
|
||||
this._register(this.preferencesRenderers.onTriggeredFuzzy(() => {
|
||||
this.searchWidget.fuzzyEnabled = true;
|
||||
this.filterPreferences();
|
||||
}));
|
||||
this.preferencesRenderers = this._register(this.instantiationService.createInstance(PreferencesRenderersController));
|
||||
|
||||
this._register(this.preferencesRenderers.onDidFilterResultsCountChange(count => this.showSearchResultsMessage(count)));
|
||||
}
|
||||
@@ -188,6 +182,10 @@ export class PreferencesEditor extends BaseEditor {
|
||||
}
|
||||
}
|
||||
|
||||
public editFocusedPreference(): void {
|
||||
this.preferencesRenderers.editFocusedPreference();
|
||||
}
|
||||
|
||||
public setInput(newInput: PreferencesEditorInput, options?: EditorOptions): TPromise<void> {
|
||||
this.defaultSettingsEditorContextKey.set(true);
|
||||
const oldInput = <PreferencesEditorInput>this.input;
|
||||
@@ -228,6 +226,7 @@ export class PreferencesEditor extends BaseEditor {
|
||||
public clearInput(): void {
|
||||
this.defaultSettingsEditorContextKey.set(false);
|
||||
this.sideBySidePreferencesWidget.clearInput();
|
||||
this.preferencesRenderers.onHidden();
|
||||
super.clearInput();
|
||||
}
|
||||
|
||||
@@ -250,15 +249,33 @@ export class PreferencesEditor extends BaseEditor {
|
||||
}
|
||||
|
||||
private onInputChanged(): void {
|
||||
if (this.searchWidget.fuzzyEnabled) {
|
||||
this.triggerThrottledFilter();
|
||||
} else {
|
||||
this.filterPreferences();
|
||||
}
|
||||
const query = this.searchWidget.getValue().trim();
|
||||
this.delayedFilterLogging.cancel();
|
||||
this.triggerSearch(query)
|
||||
.then(() => {
|
||||
const result = this.preferencesRenderers.lastFilterResult;
|
||||
if (result) {
|
||||
this.delayedFilterLogging.trigger(() => this.reportFilteringUsed(
|
||||
query,
|
||||
this.preferencesRenderers.lastFilterResult));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private triggerThrottledFilter(): void {
|
||||
this.filterThrottle.trigger(() => this.filterPreferences());
|
||||
private triggerSearch(query: string): TPromise<void> {
|
||||
if (query) {
|
||||
return TPromise.join([
|
||||
this.localSearchDelayer.trigger(() => this.preferencesRenderers.localFilterPreferences(query)),
|
||||
this.remoteSearchThrottle.trigger(() => this.progressService.showWhile(this.preferencesRenderers.remoteSearchPreferences(query), 500))
|
||||
]) as TPromise;
|
||||
} else {
|
||||
// When clearing the input, update immediately to clear it
|
||||
this.localSearchDelayer.cancel();
|
||||
this.preferencesRenderers.localFilterPreferences(query);
|
||||
|
||||
this.remoteSearchThrottle.cancel();
|
||||
return this.preferencesRenderers.remoteSearchPreferences(query);
|
||||
}
|
||||
}
|
||||
|
||||
private switchSettings(target: SettingsTarget): void {
|
||||
@@ -266,7 +283,7 @@ export class PreferencesEditor extends BaseEditor {
|
||||
if (this.editorService.getActiveEditor() !== this) {
|
||||
this.focus();
|
||||
}
|
||||
const promise = this.input.isDirty() ? this.input.save() : TPromise.as(true);
|
||||
const promise = this.input && this.input.isDirty() ? this.input.save() : TPromise.as(true);
|
||||
promise.done(value => {
|
||||
if (target === ConfigurationTarget.USER) {
|
||||
this.preferencesService.switchSettings(ConfigurationTarget.USER, this.preferencesService.userSettingsResource);
|
||||
@@ -278,133 +295,124 @@ export class PreferencesEditor extends BaseEditor {
|
||||
});
|
||||
}
|
||||
|
||||
private filterPreferences(): TPromise<void> {
|
||||
this.memento['fuzzyEnabled'] = this.searchWidget.fuzzyEnabled;
|
||||
const filter = this.searchWidget.getValue().trim();
|
||||
return this.preferencesRenderers.filterPreferences({ filter, fuzzy: this.searchWidget.fuzzyEnabled }).then(result => {
|
||||
this.showSearchResultsMessage(result.count);
|
||||
if (result.count === 0) {
|
||||
this.latestEmptyFilters.push(filter);
|
||||
}
|
||||
this.preferencesRenderers.focusFirst();
|
||||
this.delayedFilterLogging.trigger(() => this.reportFilteringUsed(filter, result.metadata));
|
||||
}, onUnexpectedError);
|
||||
}
|
||||
|
||||
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);
|
||||
private showSearchResultsMessage(count: IPreferencesCount): void {
|
||||
const countValue = count.count;
|
||||
if (count.target) {
|
||||
this.sideBySidePreferencesWidget.setResultCount(count.target, count.count);
|
||||
} else if (this.searchWidget.getValue()) {
|
||||
if (countValue === 0) {
|
||||
this.searchWidget.showMessage(nls.localize('noSettingsFound', "No Results"), countValue);
|
||||
} else if (countValue === 1) {
|
||||
this.searchWidget.showMessage(nls.localize('oneSettingFound', "1 Setting Found"), countValue);
|
||||
} else {
|
||||
this.searchWidget.showMessage(nls.localize('settingsFound', "{0} Settings matched", count), count);
|
||||
this.searchWidget.showMessage(nls.localize('settingsFound', "{0} Settings Found", countValue), countValue);
|
||||
}
|
||||
} else {
|
||||
this.searchWidget.showMessage(nls.localize('totalSettingsMessage', "Total {0} Settings", count), count);
|
||||
this.searchWidget.showMessage(nls.localize('totalSettingsMessage', "Total {0} Settings", countValue), countValue);
|
||||
}
|
||||
}
|
||||
|
||||
private reportFilteringUsed(filter: string, metadata?: IFilterMetadata): void {
|
||||
if (filter) {
|
||||
private _countById(settingsGroups: ISettingsGroup[]): IStringDictionary<number> {
|
||||
const result = {};
|
||||
|
||||
for (const group of settingsGroups) {
|
||||
let i = 0;
|
||||
for (const section of group.sections) {
|
||||
i += section.settings.length;
|
||||
}
|
||||
|
||||
result[group.id] = i;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private reportFilteringUsed(filter: string, filterResult: IFilterResult): void {
|
||||
if (filter && filter !== this._lastReportedFilter) {
|
||||
const metadata = filterResult && filterResult.metadata;
|
||||
const counts = filterResult && this._countById(filterResult.filteredGroups);
|
||||
|
||||
let durations: any;
|
||||
if (metadata) {
|
||||
durations = Object.create(null);
|
||||
Object.keys(metadata).forEach(key => durations[key] = metadata[key].duration);
|
||||
}
|
||||
|
||||
let data = {
|
||||
filter,
|
||||
emptyFilters: this.getLatestEmptyFiltersForTelemetry(),
|
||||
fuzzy: !!metadata,
|
||||
duration: metadata ? metadata.duration : undefined,
|
||||
context: metadata ? metadata.context : undefined
|
||||
durations,
|
||||
counts,
|
||||
requestCount: metadata && metadata['nlpResult'] && metadata['nlpResult'].requestCount
|
||||
};
|
||||
|
||||
this.latestEmptyFilters = [];
|
||||
/* __GDPR__
|
||||
"defaultSettings.filter" : {
|
||||
"filter": { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
|
||||
"emptyFilters" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
|
||||
"fuzzy" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
|
||||
"duration" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
|
||||
"context" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
|
||||
"durations" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
|
||||
"counts" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
|
||||
"requestCount" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
|
||||
}
|
||||
*/
|
||||
this.telemetryService.publicLog('defaultSettings.filter', data);
|
||||
this._lastReportedFilter = filter;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Put a rough limit on the size of the telemetry data, since otherwise it could be an unbounded large amount
|
||||
* of data. 8192 is the max size of a property value. This is rough since that probably includes ""s, etc.
|
||||
*/
|
||||
private getLatestEmptyFiltersForTelemetry(): string[] {
|
||||
let cumulativeSize = 0;
|
||||
return this.latestEmptyFilters.filter(filterText => (cumulativeSize += filterText.length) <= 8192);
|
||||
}
|
||||
}
|
||||
|
||||
class SettingsNavigator implements INavigator<ISetting> {
|
||||
|
||||
private iterator: ArrayNavigator<ISetting>;
|
||||
|
||||
constructor(settings: ISetting[]) {
|
||||
this.iterator = new ArrayNavigator<ISetting>(settings);
|
||||
}
|
||||
class SettingsNavigator extends ArrayNavigator<ISetting> {
|
||||
|
||||
public next(): ISetting {
|
||||
return this.iterator.next() || this.iterator.first();
|
||||
return super.next() || super.first();
|
||||
}
|
||||
|
||||
public previous(): ISetting {
|
||||
return this.iterator.previous() || this.iterator.last();
|
||||
return super.previous() || super.last();
|
||||
}
|
||||
|
||||
public parent(): ISetting {
|
||||
return this.iterator.parent();
|
||||
}
|
||||
|
||||
public first(): ISetting {
|
||||
return this.iterator.first();
|
||||
}
|
||||
|
||||
public last(): ISetting {
|
||||
return this.iterator.last();
|
||||
}
|
||||
|
||||
public current(): ISetting {
|
||||
return this.iterator.current();
|
||||
public reset(): void {
|
||||
this.index = this.start - 1;
|
||||
}
|
||||
}
|
||||
|
||||
interface ISearchCriteria {
|
||||
filter: string;
|
||||
fuzzy: boolean;
|
||||
interface IPreferencesCount {
|
||||
target?: SettingsTarget;
|
||||
count: number;
|
||||
}
|
||||
|
||||
class PreferencesRenderers extends Disposable {
|
||||
class PreferencesRenderersController 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 _remoteFilterInProgress: TPromise<any>;
|
||||
private _prefsModelsForSearch = new Map<string, ISettingsEditorModel>();
|
||||
|
||||
private _onTriggeredFuzzy: Emitter<void> = this._register(new Emitter<void>());
|
||||
public onTriggeredFuzzy: Event<void> = this._onTriggeredFuzzy.event;
|
||||
private _currentLocalSearchProvider: ISearchProvider;
|
||||
private _currentRemoteSearchProvider: ISearchProvider;
|
||||
private _lastQuery: string;
|
||||
private _lastFilterResult: IFilterResult;
|
||||
|
||||
private _onDidFilterResultsCountChange: Emitter<number> = this._register(new Emitter<number>());
|
||||
public onDidFilterResultsCountChange: Event<number> = this._onDidFilterResultsCountChange.event;
|
||||
private _onDidFilterResultsCountChange: Emitter<IPreferencesCount> = this._register(new Emitter<IPreferencesCount>());
|
||||
public onDidFilterResultsCountChange: Event<IPreferencesCount> = this._onDidFilterResultsCountChange.event;
|
||||
|
||||
constructor(
|
||||
private preferencesSearchService: IPreferencesSearchService
|
||||
@IPreferencesSearchService private preferencesSearchService: IPreferencesSearchService,
|
||||
@ITelemetryService private telemetryService: ITelemetryService,
|
||||
@IPreferencesService private preferencesService: IPreferencesService,
|
||||
@IWorkspaceContextService private workspaceContextService: IWorkspaceContextService,
|
||||
@ILogService private logService: ILogService
|
||||
) {
|
||||
super();
|
||||
}
|
||||
|
||||
get lastFilterResult(): IFilterResult {
|
||||
return this._lastFilterResult;
|
||||
}
|
||||
|
||||
get defaultPreferencesRenderer(): IPreferencesRenderer<ISetting> {
|
||||
return this._defaultPreferencesRenderer;
|
||||
}
|
||||
@@ -420,12 +428,12 @@ class PreferencesRenderers extends Disposable {
|
||||
this._defaultPreferencesRendererDisposables = dispose(this._defaultPreferencesRendererDisposables);
|
||||
|
||||
if (this._defaultPreferencesRenderer) {
|
||||
this._defaultPreferencesRenderer.onUpdatePreference(({ key, value, source, index }) => this._updatePreference(key, value, source, index, this._editablePreferencesRenderer), this, this._defaultPreferencesRendererDisposables);
|
||||
this._defaultPreferencesRenderer.onUpdatePreference(({ key, value, source }) => {
|
||||
this._editablePreferencesRenderer.updatePreference(key, value, source);
|
||||
this._updatePreference(key, value, source);
|
||||
}, 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()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -435,42 +443,136 @@ class PreferencesRenderers extends Disposable {
|
||||
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);
|
||||
(<ISettingsEditorModel>this._editablePreferencesRenderer.preferencesModel)
|
||||
.onDidChangeGroups(this._onEditableContentDidChange, this, this._editablePreferencesRendererDisposables);
|
||||
|
||||
this._editablePreferencesRenderer.onUpdatePreference(({ key, value, source }) => this._updatePreference(key, value, source, true), this, this._defaultPreferencesRendererDisposables);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
filterPreferences(criteria: ISearchCriteria): TPromise<{ count: number, metadata: IFilterMetadata }> {
|
||||
this._searchCriteria = criteria;
|
||||
private async _onEditableContentDidChange(): TPromise<void> {
|
||||
await this.localFilterPreferences(this._lastQuery, true);
|
||||
await this.remoteSearchPreferences(this._lastQuery, true);
|
||||
}
|
||||
|
||||
if (this._filtersInProgress) {
|
||||
onHidden(): void {
|
||||
this._prefsModelsForSearch.forEach(model => model.dispose());
|
||||
this._prefsModelsForSearch = new Map<string, ISettingsEditorModel>();
|
||||
}
|
||||
|
||||
remoteSearchPreferences(query: string, updateCurrentResults?: boolean): TPromise<void> {
|
||||
if (this._remoteFilterInProgress && this._remoteFilterInProgress.cancel) {
|
||||
// Resolved/rejected promises have no .cancel()
|
||||
this._filtersInProgress.forEach(p => p.cancel && p.cancel());
|
||||
this._remoteFilterInProgress.cancel();
|
||||
}
|
||||
|
||||
this._currentSearchModel = this.preferencesSearchService.startSearch(this._searchCriteria.filter, criteria.fuzzy);
|
||||
this._filtersInProgress = [this._filterDefaultPreferences(), this._filterEditablePreferences()];
|
||||
this._currentRemoteSearchProvider = (updateCurrentResults && this._currentRemoteSearchProvider) || this.preferencesSearchService.getRemoteSearchProvider(query);
|
||||
|
||||
return TPromise.join<IFilterResult>(this._filtersInProgress).then(() => {
|
||||
const count = this.consolidateAndUpdate();
|
||||
return { count, metadata: this._defaultPreferencesFilterResult && this._defaultPreferencesFilterResult.metadata };
|
||||
this._remoteFilterInProgress = this.filterOrSearchPreferences(query, this._currentRemoteSearchProvider, 'nlpResult', nls.localize('nlpResult', "Natural Language Results"), 1, updateCurrentResults);
|
||||
|
||||
return this._remoteFilterInProgress.then(() => {
|
||||
this._remoteFilterInProgress = null;
|
||||
}, err => {
|
||||
if (isPromiseCanceledError(err)) {
|
||||
return null;
|
||||
} else {
|
||||
onUnexpectedError(err);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
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);
|
||||
localFilterPreferences(query: string, updateCurrentResults?: boolean): TPromise<void> {
|
||||
if (this._settingsNavigator) {
|
||||
this._settingsNavigator.reset();
|
||||
}
|
||||
|
||||
this._settingsNavigator.first(); // Move to first
|
||||
this._currentLocalSearchProvider = (updateCurrentResults && this._currentLocalSearchProvider) || this.preferencesSearchService.getLocalSearchProvider(query);
|
||||
return this.filterOrSearchPreferences(query, this._currentLocalSearchProvider, 'filterResult', nls.localize('filterResult', "Filtered Results"), 0, updateCurrentResults);
|
||||
}
|
||||
|
||||
private filterOrSearchPreferences(query: string, searchProvider: ISearchProvider, groupId: string, groupLabel: string, groupOrder: number, editableContentOnly?: boolean): TPromise<void> {
|
||||
this._lastQuery = query;
|
||||
|
||||
const filterPs: TPromise<any>[] = [this._filterOrSearchPreferences(query, this.editablePreferencesRenderer, searchProvider, groupId, groupLabel, groupOrder)];
|
||||
if (!editableContentOnly) {
|
||||
filterPs.push(
|
||||
this._filterOrSearchPreferences(query, this.defaultPreferencesRenderer, searchProvider, groupId, groupLabel, groupOrder));
|
||||
}
|
||||
|
||||
filterPs.push(this.searchAllSettingsTargets(query, searchProvider, groupId, groupLabel, groupOrder));
|
||||
|
||||
return TPromise.join(filterPs).then(results => {
|
||||
let [editableFilterResult, defaultFilterResult] = results;
|
||||
|
||||
if (!defaultFilterResult && editableContentOnly) {
|
||||
defaultFilterResult = this.lastFilterResult;
|
||||
}
|
||||
|
||||
this.consolidateAndUpdate(defaultFilterResult, editableFilterResult);
|
||||
if (defaultFilterResult) {
|
||||
this._lastFilterResult = defaultFilterResult;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private searchAllSettingsTargets(query: string, searchProvider: ISearchProvider, groupId: string, groupLabel: string, groupOrder: number): TPromise<void> {
|
||||
const searchPs = [
|
||||
this.searchSettingsTarget(query, searchProvider, ConfigurationTarget.WORKSPACE, groupId, groupLabel, groupOrder),
|
||||
this.searchSettingsTarget(query, searchProvider, ConfigurationTarget.USER, groupId, groupLabel, groupOrder)
|
||||
];
|
||||
|
||||
for (const folder of this.workspaceContextService.getWorkspace().folders) {
|
||||
const folderSettingsResource = this.preferencesService.getFolderSettingsResource(folder.uri);
|
||||
searchPs.push(this.searchSettingsTarget(query, searchProvider, folderSettingsResource, groupId, groupLabel, groupOrder));
|
||||
}
|
||||
|
||||
|
||||
return TPromise.join(searchPs).then(() => { });
|
||||
}
|
||||
|
||||
private searchSettingsTarget(query: string, provider: ISearchProvider, target: SettingsTarget, groupId: string, groupLabel: string, groupOrder: number): TPromise<void> {
|
||||
if (!query) {
|
||||
// Don't open the other settings targets when query is empty
|
||||
this._onDidFilterResultsCountChange.fire({ target, count: 0 });
|
||||
return TPromise.wrap(null);
|
||||
}
|
||||
|
||||
return this.getPreferencesEditorModel(target).then(model => {
|
||||
return model && this._filterOrSearchPreferencesModel('', <ISettingsEditorModel>model, provider, groupId, groupLabel, groupOrder);
|
||||
}).then(result => {
|
||||
const count = result ? this._flatten(result.filteredGroups).length : 0;
|
||||
this._onDidFilterResultsCountChange.fire({ target, count });
|
||||
}, err => {
|
||||
if (!isPromiseCanceledError(err)) {
|
||||
return TPromise.wrapError(err);
|
||||
}
|
||||
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
private async getPreferencesEditorModel(target: SettingsTarget): TPromise<ISettingsEditorModel | null> {
|
||||
const resource = target === ConfigurationTarget.USER ? this.preferencesService.userSettingsResource :
|
||||
target === ConfigurationTarget.WORKSPACE ? this.preferencesService.workspaceSettingsResource :
|
||||
target;
|
||||
|
||||
if (!resource) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const targetKey = resource.toString();
|
||||
if (!this._prefsModelsForSearch.has(targetKey)) {
|
||||
try {
|
||||
const model = this._register(await this.preferencesService.createPreferencesEditorModel(resource));
|
||||
this._prefsModelsForSearch.set(targetKey, <ISettingsEditorModel>model);
|
||||
} catch (e) {
|
||||
// Will throw when the settings file doesn't exist.
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
return this._prefsModelsForSearch.get(targetKey);
|
||||
}
|
||||
|
||||
focusNextPreference(forward: boolean = true) {
|
||||
@@ -483,56 +585,99 @@ 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; });
|
||||
editFocusedPreference(): void {
|
||||
if (!this._settingsNavigator || !this._settingsNavigator.current()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const setting = this._settingsNavigator.current();
|
||||
const shownInEditableRenderer = this._editablePreferencesRenderer.editPreference(setting);
|
||||
if (!shownInEditableRenderer) {
|
||||
this.defaultPreferencesRenderer.editPreference(setting);
|
||||
}
|
||||
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; });
|
||||
private _filterOrSearchPreferences(filter: string, preferencesRenderer: IPreferencesRenderer<ISetting>, provider: ISearchProvider, groupId: string, groupLabel: string, groupOrder: number): TPromise<IFilterResult> {
|
||||
if (!preferencesRenderer) {
|
||||
return TPromise.wrap(null);
|
||||
}
|
||||
return TPromise.wrap(null);
|
||||
|
||||
const model = <ISettingsEditorModel>preferencesRenderer.preferencesModel;
|
||||
return this._filterOrSearchPreferencesModel(filter, model, provider, groupId, groupLabel, groupOrder).then(filterResult => {
|
||||
preferencesRenderer.filterPreferences(filterResult);
|
||||
return filterResult;
|
||||
});
|
||||
}
|
||||
|
||||
private _getFirstSettingFromTheGroups(allGroups: ISettingsGroup[]): ISetting {
|
||||
if (allGroups.length) {
|
||||
if (allGroups[0].sections.length) {
|
||||
return allGroups[0].sections[0].settings[0];
|
||||
}
|
||||
private _filterOrSearchPreferencesModel(filter: string, model: ISettingsEditorModel, provider: ISearchProvider, groupId: string, groupLabel: string, groupOrder: number): TPromise<IFilterResult> {
|
||||
const searchP = provider ? provider.searchModel(model) : TPromise.wrap(null);
|
||||
return searchP
|
||||
.then<ISearchResult>(null, err => {
|
||||
if (isPromiseCanceledError(err)) {
|
||||
return TPromise.wrapError(err);
|
||||
} else {
|
||||
/* __GDPR__
|
||||
"defaultSettings.searchError" : {
|
||||
"message": { "classification": "CallstackOrException", "purpose": "FeatureInsight" },
|
||||
"filter": { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
|
||||
}
|
||||
*/
|
||||
const message = getErrorMessage(err).trim();
|
||||
if (message && message !== 'Error') {
|
||||
// "Error" = any generic network error
|
||||
this.telemetryService.publicLog('defaultSettings.searchError', { message, filter });
|
||||
this.logService.info('Setting search error: ' + message);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
})
|
||||
.then(searchResult => {
|
||||
const filterResult = searchResult ?
|
||||
model.updateResultGroup(groupId, {
|
||||
id: groupId,
|
||||
label: groupLabel,
|
||||
result: searchResult,
|
||||
order: groupOrder
|
||||
}) :
|
||||
model.updateResultGroup(groupId, null);
|
||||
|
||||
if (filterResult) {
|
||||
filterResult.query = filter;
|
||||
}
|
||||
|
||||
return filterResult;
|
||||
});
|
||||
}
|
||||
|
||||
private consolidateAndUpdate(defaultFilterResult: IFilterResult, editableFilterResult: IFilterResult): void {
|
||||
const defaultPreferencesFilteredGroups = defaultFilterResult ? defaultFilterResult.filteredGroups : this._getAllPreferences(this._defaultPreferencesRenderer);
|
||||
const editablePreferencesFilteredGroups = editableFilterResult ? editableFilterResult.filteredGroups : this._getAllPreferences(this._editablePreferencesRenderer);
|
||||
const consolidatedSettings = this._consolidateSettings(editablePreferencesFilteredGroups, defaultPreferencesFilteredGroups);
|
||||
|
||||
// Maintain the current navigation position when updating SettingsNavigator
|
||||
const current = this._settingsNavigator && this._settingsNavigator.current();
|
||||
const navigatorSettings = this._lastQuery ? consolidatedSettings : [];
|
||||
const currentIndex = current ?
|
||||
arrays.firstIndex(navigatorSettings, s => s.key === current.key) :
|
||||
-1;
|
||||
|
||||
this._settingsNavigator = new SettingsNavigator(navigatorSettings, Math.max(currentIndex, 0));
|
||||
|
||||
if (currentIndex >= 0) {
|
||||
this._settingsNavigator.next();
|
||||
const newCurrent = this._settingsNavigator.current();
|
||||
this._focusPreference(newCurrent, this._defaultPreferencesRenderer);
|
||||
this._focusPreference(newCurrent, this._editablePreferencesRenderer);
|
||||
}
|
||||
return null;
|
||||
|
||||
const totalCount = consolidatedSettings.length;
|
||||
this._onDidFilterResultsCountChange.fire({ count: totalCount });
|
||||
}
|
||||
|
||||
private _getAllPreferences(preferencesRenderer: IPreferencesRenderer<ISetting>): ISettingsGroup[] {
|
||||
return preferencesRenderer ? (<ISettingsEditorModel>preferencesRenderer.preferencesModel).settingsGroups : [];
|
||||
}
|
||||
|
||||
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, this.preferencesSearchService.remoteSearchAllowed);
|
||||
return filterResult;
|
||||
});
|
||||
}
|
||||
return TPromise.as(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 {
|
||||
if (preference && preferencesRenderer) {
|
||||
preferencesRenderer.focusPreference(preference);
|
||||
@@ -545,15 +690,63 @@ class PreferencesRenderers extends Disposable {
|
||||
}
|
||||
}
|
||||
|
||||
private _updatePreference(key: string, value: any, source: ISetting, index: number, preferencesRenderer: IPreferencesRenderer<ISetting>): void {
|
||||
if (preferencesRenderer) {
|
||||
preferencesRenderer.updatePreference(key, value, source, index);
|
||||
private _updatePreference(key: string, value: any, source: ISetting, fromEditableSettings?: boolean): void {
|
||||
const data = {
|
||||
userConfigurationKeys: [key]
|
||||
};
|
||||
|
||||
if (this.lastFilterResult) {
|
||||
data['query'] = this.lastFilterResult.query;
|
||||
data['editableSide'] = !!fromEditableSettings;
|
||||
|
||||
const nlpMetadata = this.lastFilterResult.metadata && this.lastFilterResult.metadata['nlpResult'];
|
||||
if (nlpMetadata) {
|
||||
const sortedKeys = Object.keys(nlpMetadata.scoredResults).sort((a, b) => nlpMetadata.scoredResults[b].score - nlpMetadata.scoredResults[a].score);
|
||||
const suffix = '##' + key;
|
||||
data['nlpIndex'] = arrays.firstIndex(sortedKeys, key => strings.endsWith(key, suffix));
|
||||
}
|
||||
|
||||
const settingLocation = this._findSetting(this.lastFilterResult, key);
|
||||
if (settingLocation) {
|
||||
data['groupId'] = this.lastFilterResult.filteredGroups[settingLocation.groupIdx].id;
|
||||
data['displayIdx'] = settingLocation.overallSettingIdx;
|
||||
}
|
||||
}
|
||||
|
||||
/* __GDPR__
|
||||
"defaultSettingsActions.copySetting" : {
|
||||
"userConfigurationKeys" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
|
||||
"query" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
|
||||
"nlpIndex" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
|
||||
"groupId" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
|
||||
"displayIdx" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
|
||||
"editableSide" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
|
||||
}
|
||||
*/
|
||||
this.telemetryService.publicLog('defaultSettingsActions.copySetting', data);
|
||||
}
|
||||
|
||||
private _findSetting(filterResult: IFilterResult, key: string): { groupIdx: number, settingIdx: number, overallSettingIdx: number } {
|
||||
let overallSettingIdx = 0;
|
||||
|
||||
for (let groupIdx = 0; groupIdx < filterResult.filteredGroups.length; groupIdx++) {
|
||||
const group = filterResult.filteredGroups[groupIdx];
|
||||
for (let settingIdx = 0; settingIdx < group.sections[0].settings.length; settingIdx++) {
|
||||
const setting = group.sections[0].settings[settingIdx];
|
||||
if (key === setting.key) {
|
||||
return { groupIdx, settingIdx, overallSettingIdx };
|
||||
}
|
||||
|
||||
overallSettingIdx++;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
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));
|
||||
const defaultSettings = this._flatten(defaultSettingsGroups);
|
||||
const editableSettings = this._flatten(editableSettingsGroups).filter(secondarySetting => defaultSettings.every(primarySetting => primarySetting.key !== secondarySetting.key));
|
||||
return [...defaultSettings, ...editableSettings];
|
||||
}
|
||||
|
||||
@@ -564,6 +757,7 @@ class PreferencesRenderers extends Disposable {
|
||||
settings.push(...section.settings);
|
||||
}
|
||||
}
|
||||
|
||||
return settings;
|
||||
}
|
||||
|
||||
@@ -659,6 +853,10 @@ class SideBySidePreferencesWidget extends Widget {
|
||||
});
|
||||
}
|
||||
|
||||
public setResultCount(settingsTarget: SettingsTarget, count: number): void {
|
||||
this.settingsTargetsWidget.setResultCount(settingsTarget, count);
|
||||
}
|
||||
|
||||
public layout(dimension: Dimension): void {
|
||||
this.dimension = dimension;
|
||||
this.sash.setDimenesion(this.dimension);
|
||||
@@ -776,7 +974,7 @@ class SideBySidePreferencesWidget extends Widget {
|
||||
|
||||
export class DefaultPreferencesEditor extends BaseTextEditor {
|
||||
|
||||
public static ID: string = 'workbench.editor.defaultPreferences';
|
||||
public static readonly ID: string = 'workbench.editor.defaultPreferences';
|
||||
|
||||
constructor(
|
||||
@ITelemetryService telemetryService: ITelemetryService,
|
||||
@@ -905,7 +1103,7 @@ abstract class AbstractSettingsEditorContribution extends Disposable implements
|
||||
|
||||
private _hasAssociatedPreferencesModelChanged(associatedPreferencesModelUri: URI): TPromise<boolean> {
|
||||
return this.preferencesRendererCreationPromise.then(preferencesRenderer => {
|
||||
return !(preferencesRenderer && preferencesRenderer.associatedPreferencesModel && preferencesRenderer.associatedPreferencesModel.uri.toString() === associatedPreferencesModelUri.toString());
|
||||
return !(preferencesRenderer && preferencesRenderer.getAssociatedPreferencesModel() && preferencesRenderer.getAssociatedPreferencesModel().uri.toString() === associatedPreferencesModelUri.toString());
|
||||
});
|
||||
}
|
||||
|
||||
@@ -914,10 +1112,11 @@ abstract class AbstractSettingsEditorContribution extends Disposable implements
|
||||
.then(associatedPreferencesEditorModel => {
|
||||
return this.preferencesRendererCreationPromise.then(preferencesRenderer => {
|
||||
if (preferencesRenderer) {
|
||||
if (preferencesRenderer.associatedPreferencesModel) {
|
||||
preferencesRenderer.associatedPreferencesModel.dispose();
|
||||
const associatedPreferencesModel = preferencesRenderer.getAssociatedPreferencesModel();
|
||||
if (associatedPreferencesModel) {
|
||||
associatedPreferencesModel.dispose();
|
||||
}
|
||||
preferencesRenderer.associatedPreferencesModel = associatedPreferencesEditorModel;
|
||||
preferencesRenderer.setAssociatedPreferencesModel(associatedPreferencesEditorModel);
|
||||
}
|
||||
return preferencesRenderer;
|
||||
});
|
||||
@@ -928,8 +1127,9 @@ abstract class AbstractSettingsEditorContribution extends Disposable implements
|
||||
if (this.preferencesRendererCreationPromise) {
|
||||
this.preferencesRendererCreationPromise.then(preferencesRenderer => {
|
||||
if (preferencesRenderer) {
|
||||
if (preferencesRenderer.associatedPreferencesModel) {
|
||||
preferencesRenderer.associatedPreferencesModel.dispose();
|
||||
const associatedPreferencesModel = preferencesRenderer.getAssociatedPreferencesModel();
|
||||
if (associatedPreferencesModel) {
|
||||
associatedPreferencesModel.dispose();
|
||||
}
|
||||
preferencesRenderer.preferencesModel.dispose();
|
||||
preferencesRenderer.dispose();
|
||||
@@ -950,7 +1150,7 @@ abstract class AbstractSettingsEditorContribution extends Disposable implements
|
||||
|
||||
class DefaultSettingsEditorContribution extends AbstractSettingsEditorContribution implements ISettingsEditorContribution {
|
||||
|
||||
static ID: string = 'editor.contrib.defaultsettings';
|
||||
static readonly ID: string = 'editor.contrib.defaultsettings';
|
||||
|
||||
getId(): string {
|
||||
return DefaultSettingsEditorContribution.ID;
|
||||
@@ -971,7 +1171,7 @@ class DefaultSettingsEditorContribution extends AbstractSettingsEditorContributi
|
||||
|
||||
class SettingsEditorContribution extends AbstractSettingsEditorContribution implements ISettingsEditorContribution {
|
||||
|
||||
static ID: string = 'editor.contrib.settings';
|
||||
static readonly ID: string = 'editor.contrib.settings';
|
||||
|
||||
constructor(editor: ICodeEditor,
|
||||
@IInstantiationService instantiationService: IInstantiationService,
|
||||
@@ -1136,3 +1336,20 @@ const focusPreviousSearchResultCommand = new FocusPreviousSearchResultCommand({
|
||||
kbOpts: { primary: KeyMod.Shift | KeyCode.Enter }
|
||||
});
|
||||
KeybindingsRegistry.registerCommandAndKeybindingRule(focusPreviousSearchResultCommand.toCommandAndKeybindingRule(KeybindingsRegistry.WEIGHT.editorContrib()));
|
||||
|
||||
class EditFocusedSettingCommand extends SettingsCommand {
|
||||
|
||||
public runCommand(accessor: ServicesAccessor, args: any): void {
|
||||
const preferencesEditor = this.getPreferencesEditor(accessor);
|
||||
if (preferencesEditor) {
|
||||
preferencesEditor.editFocusedPreference();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
const editFocusedSettingCommand = new EditFocusedSettingCommand({
|
||||
id: SETTINGS_EDITOR_COMMAND_EDIT_FOCUSED_SETTING,
|
||||
precondition: CONTEXT_SETTINGS_SEARCH_FOCUS,
|
||||
kbOpts: { primary: KeyMod.CtrlCmd | KeyCode.US_DOT }
|
||||
});
|
||||
KeybindingsRegistry.registerCommandAndKeybindingRule(editFocusedSettingCommand.toCommandAndKeybindingRule(KeybindingsRegistry.WEIGHT.editorContrib()));
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -17,10 +17,9 @@ 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 { IModel } from 'vs/editor/common/editorCommon';
|
||||
import { ITextModel } from 'vs/editor/common/model';
|
||||
import { IEditorGroupService } from 'vs/workbench/services/group/common/groupService';
|
||||
import { IFileService, FileOperationError, FileOperationResult } from 'vs/platform/files/common/files';
|
||||
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';
|
||||
@@ -40,6 +39,7 @@ import { ConfigurationTarget } from 'vs/platform/configuration/common/configurat
|
||||
import { IModeService } from 'vs/editor/common/services/modeService';
|
||||
import { parse } from 'vs/base/common/json';
|
||||
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
|
||||
import { INotificationService } from 'vs/platform/notification/common/notification';
|
||||
|
||||
const emptyEditableSettingsContent = '{\n}';
|
||||
|
||||
@@ -61,7 +61,7 @@ export class PreferencesService extends Disposable implements IPreferencesServic
|
||||
@IEditorGroupService private editorGroupService: IEditorGroupService,
|
||||
@IFileService private fileService: IFileService,
|
||||
@IWorkspaceConfigurationService private configurationService: IWorkspaceConfigurationService,
|
||||
@IMessageService private messageService: IMessageService,
|
||||
@INotificationService private notificationService: INotificationService,
|
||||
@IWorkspaceContextService private contextService: IWorkspaceContextService,
|
||||
@IInstantiationService private instantiationService: IInstantiationService,
|
||||
@IEnvironmentService private environmentService: IEnvironmentService,
|
||||
@@ -107,7 +107,7 @@ export class PreferencesService extends Disposable implements IPreferencesServic
|
||||
return this.getEditableSettingsURI(ConfigurationTarget.WORKSPACE_FOLDER, resource);
|
||||
}
|
||||
|
||||
resolveModel(uri: URI): TPromise<IModel> {
|
||||
resolveModel(uri: URI): TPromise<ITextModel> {
|
||||
if (this.isDefaultSettingsResource(uri) || this.isDefaultResourceSettingsResource(uri)) {
|
||||
|
||||
const scope = this.isDefaultSettingsResource(uri) ? ConfigurationScope.WINDOW : ConfigurationScope.RESOURCE;
|
||||
@@ -185,7 +185,7 @@ export class PreferencesService extends Disposable implements IPreferencesServic
|
||||
|
||||
openWorkspaceSettings(options?: IEditorOptions, position?: EditorPosition): TPromise<IEditor> {
|
||||
if (this.contextService.getWorkbenchState() === WorkbenchState.EMPTY) {
|
||||
this.messageService.show(Severity.Info, nls.localize('openFolderFirst', "Open a folder first to create workspace settings"));
|
||||
this.notificationService.info(nls.localize('openFolderFirst', "Open a folder first to create workspace settings"));
|
||||
return TPromise.as(null);
|
||||
}
|
||||
return this.doOpenSettings(ConfigurationTarget.WORKSPACE, this.workspaceSettingsResource, options, position);
|
||||
@@ -197,8 +197,7 @@ export class PreferencesService extends Disposable implements IPreferencesServic
|
||||
|
||||
switchSettings(target: ConfigurationTarget, resource: URI): TPromise<void> {
|
||||
const activeEditor = this.editorService.getActiveEditor();
|
||||
const activeEditorInput = activeEditor.input;
|
||||
if (activeEditorInput instanceof PreferencesEditorInput) {
|
||||
if (activeEditor && activeEditor.input instanceof PreferencesEditorInput) {
|
||||
return this.getOrCreateEditableSettingsEditorInput(target, this.getEditableSettingsURI(target, resource))
|
||||
.then(toInput => {
|
||||
const replaceWith = new PreferencesEditorInput(this.getPreferencesEditorInputName(target, resource), toInput.getDescription(), this.instantiationService.createInstance(DefaultPreferencesEditorInput, this.getDefaultSettingsResource(target)), toInput);
|
||||
|
||||
@@ -10,12 +10,10 @@ import * as DOM from 'vs/base/browser/dom';
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
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, 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';
|
||||
@@ -23,7 +21,7 @@ import { ISettingsGroup } from 'vs/workbench/parts/preferences/common/preference
|
||||
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
|
||||
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 { attachInputBoxStyler, attachStylerCallback } 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';
|
||||
@@ -33,8 +31,8 @@ import { Separator, ActionBar, ActionsOrientation, BaseActionItem } from 'vs/bas
|
||||
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';
|
||||
import { IModelDeltaDecoration, TrackedRangeStickiness } from 'vs/editor/common/model';
|
||||
|
||||
export class SettingsHeaderWidget extends Widget implements IViewZone {
|
||||
|
||||
@@ -103,32 +101,20 @@ export class SettingsHeaderWidget extends Widget implements IViewZone {
|
||||
|
||||
export class DefaultSettingsHeaderWidget extends SettingsHeaderWidget {
|
||||
|
||||
private linkElement: HTMLElement;
|
||||
private _onClick = this._register(new Emitter<void>());
|
||||
public onClick: Event<void> = this._onClick.event;
|
||||
|
||||
protected create() {
|
||||
super.create();
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
public toggleMessage(hasSettings: boolean, promptFuzzy = false): void {
|
||||
public toggleMessage(hasSettings: boolean): void {
|
||||
if (hasSettings) {
|
||||
this.setMessage(localize('defaultSettings', "Place your settings in the right hand side editor to override."));
|
||||
DOM.addClass(this.linkElement, 'hidden');
|
||||
} else {
|
||||
this.setMessage(localize('noSettingsFound', "No Settings Found."));
|
||||
|
||||
if (promptFuzzy) {
|
||||
DOM.removeClass(this.linkElement, 'hidden');
|
||||
} else {
|
||||
DOM.addClass(this.linkElement, 'hidden');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -295,6 +281,7 @@ export class SettingsGroupTitleWidget extends Widget implements IViewZone {
|
||||
export class FolderSettingsActionItem extends BaseActionItem {
|
||||
|
||||
private _folder: IWorkspaceFolder;
|
||||
private _folderSettingCounts = new Map<string, number>();
|
||||
|
||||
private container: HTMLElement;
|
||||
private anchorElement: HTMLElement;
|
||||
@@ -324,6 +311,12 @@ export class FolderSettingsActionItem extends BaseActionItem {
|
||||
this.update();
|
||||
}
|
||||
|
||||
public setCount(settingsTarget: URI, count: number): void {
|
||||
const folder = this.contextService.getWorkspaceFolder(settingsTarget).uri;
|
||||
this._folderSettingCounts.set(folder.toString(), count);
|
||||
this.update();
|
||||
}
|
||||
|
||||
public render(container: HTMLElement): void {
|
||||
this.builder = $(container);
|
||||
|
||||
@@ -391,14 +384,19 @@ export class FolderSettingsActionItem extends BaseActionItem {
|
||||
}
|
||||
|
||||
private update(): void {
|
||||
let total = 0;
|
||||
this._folderSettingCounts.forEach(n => total += n);
|
||||
|
||||
const workspace = this.contextService.getWorkspace();
|
||||
if (this._folder) {
|
||||
this.labelElement.textContent = this._folder.name;
|
||||
this.anchorElement.title = this._folder.name;
|
||||
this.detailsElement.textContent = this._action.label;
|
||||
const detailsText = this.labelWithCount(this._action.label, total);
|
||||
this.detailsElement.textContent = detailsText;
|
||||
DOM.toggleClass(this.dropDownElement, 'hide', workspace.folders.length === 1 || !this._action.checked);
|
||||
} else {
|
||||
this.labelElement.textContent = this._action.label;
|
||||
const labelText = this.labelWithCount(this._action.label, total);
|
||||
this.labelElement.textContent = labelText;
|
||||
this.detailsElement.textContent = '';
|
||||
this.anchorElement.title = this._action.label;
|
||||
DOM.removeClass(this.dropDownElement, 'hide');
|
||||
@@ -424,9 +422,10 @@ export class FolderSettingsActionItem extends BaseActionItem {
|
||||
if (this.contextService.getWorkbenchState() === WorkbenchState.WORKSPACE && workspaceFolders.length > 0) {
|
||||
actions.push(new Separator());
|
||||
actions.push(...workspaceFolders.map((folder, index) => {
|
||||
const folderCount = this._folderSettingCounts.get(folder.uri.toString());
|
||||
return <IAction>{
|
||||
id: 'folderSettingsTarget' + index,
|
||||
label: folder.name,
|
||||
label: this.labelWithCount(folder.name, folderCount),
|
||||
checked: this.folder && this.folder.uri.toString() === folder.uri.toString(),
|
||||
enabled: true,
|
||||
run: () => this._action.run(folder)
|
||||
@@ -436,6 +435,15 @@ export class FolderSettingsActionItem extends BaseActionItem {
|
||||
return actions;
|
||||
}
|
||||
|
||||
private labelWithCount(label: string, count: number | undefined): string {
|
||||
// Append the count if it's >0 and not undefined
|
||||
if (count) {
|
||||
label += ` (${count})`;
|
||||
}
|
||||
|
||||
return label;
|
||||
}
|
||||
|
||||
public dispose(): void {
|
||||
dispose(this.disposables);
|
||||
super.dispose();
|
||||
@@ -506,6 +514,26 @@ export class SettingsTargetsWidget extends Widget {
|
||||
}
|
||||
}
|
||||
|
||||
public setResultCount(settingsTarget: SettingsTarget, count: number): void {
|
||||
if (settingsTarget === ConfigurationTarget.WORKSPACE) {
|
||||
let label = localize('workspaceSettings', "Workspace Settings");
|
||||
if (count) {
|
||||
label += ` (${count})`;
|
||||
}
|
||||
|
||||
this.workspaceSettings.label = label;
|
||||
} else if (settingsTarget === ConfigurationTarget.USER) {
|
||||
let label = localize('userSettings', "User Settings");
|
||||
if (count) {
|
||||
label += ` (${count})`;
|
||||
}
|
||||
|
||||
this.userSettings.label = label;
|
||||
} else if (settingsTarget instanceof URI) {
|
||||
this.folderSettings.setCount(settingsTarget, count);
|
||||
}
|
||||
}
|
||||
|
||||
private onWorkbenchStateChanged(): void {
|
||||
this.folderSettings.folder = null;
|
||||
this.update();
|
||||
@@ -533,7 +561,6 @@ export class SettingsTargetsWidget extends Widget {
|
||||
|
||||
export interface SearchOptions extends IInputOptions {
|
||||
focusKey?: IContextKey<boolean>;
|
||||
showFuzzyToggle?: boolean;
|
||||
showResultCount?: boolean;
|
||||
}
|
||||
|
||||
@@ -544,7 +571,6 @@ export class SearchWidget extends Widget {
|
||||
private countElement: HTMLElement;
|
||||
private searchContainer: HTMLElement;
|
||||
private inputBox: InputBox;
|
||||
private fuzzyToggle: Checkbox;
|
||||
private controlsDiv: HTMLElement;
|
||||
|
||||
private _onDidChange: Emitter<string> = this._register(new Emitter<string>());
|
||||
@@ -562,32 +588,10 @@ export class SearchWidget extends Widget {
|
||||
this.create(parent);
|
||||
}
|
||||
|
||||
public get fuzzyEnabled(): boolean {
|
||||
return this.fuzzyToggle.checked && this.fuzzyToggle.enabled;
|
||||
}
|
||||
|
||||
public set fuzzyEnabled(value: boolean) {
|
||||
this.fuzzyToggle.checked = value;
|
||||
}
|
||||
|
||||
private create(parent: HTMLElement) {
|
||||
this.domNode = DOM.append(parent, DOM.$('div.settings-header-widget'));
|
||||
this.createSearchContainer(DOM.append(this.domNode, DOM.$('div.settings-search-container')));
|
||||
this.controlsDiv = DOM.append(this.domNode, DOM.$('div.settings-search-controls'));
|
||||
if (this.options.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));
|
||||
}
|
||||
|
||||
if (this.options.showResultCount) {
|
||||
this.countElement = DOM.append(this.controlsDiv, DOM.$('.settings-count-widget'));
|
||||
@@ -639,16 +643,6 @@ export class SearchWidget extends Widget {
|
||||
}
|
||||
}
|
||||
|
||||
public setFuzzyToggleVisible(visible: boolean): void {
|
||||
if (visible) {
|
||||
this.fuzzyToggle.domNode.classList.remove('hidden');
|
||||
this.fuzzyToggle.enable();
|
||||
} else {
|
||||
this.fuzzyToggle.domNode.classList.add('hidden');
|
||||
this.fuzzyToggle.disable();
|
||||
}
|
||||
}
|
||||
|
||||
private styleCountElementForeground() {
|
||||
const colorId = DOM.hasClass(this.countElement, 'no-results') ? errorForeground : badgeForeground;
|
||||
const color = this.themeService.getTheme().getColor(colorId);
|
||||
@@ -673,8 +667,7 @@ export class SearchWidget extends Widget {
|
||||
|
||||
private getControlsWidth(): number {
|
||||
const countWidth = this.countElement ? DOM.getTotalWidth(this.countElement) : 0;
|
||||
const fuzzyToggleWidth = this.fuzzyToggle ? DOM.getTotalWidth(this.fuzzyToggle.domNode) : 0;
|
||||
return countWidth + fuzzyToggleWidth + 20;
|
||||
return countWidth + 20;
|
||||
}
|
||||
|
||||
public focus() {
|
||||
@@ -799,13 +792,13 @@ export class EditPreferenceWidget<T> extends Disposable {
|
||||
|
||||
show(line: number, hoverMessage: string, preferences: T[]): void {
|
||||
this._preferences = preferences;
|
||||
const newDecoration: editorCommon.IModelDeltaDecoration[] = [];
|
||||
const newDecoration: IModelDeltaDecoration[] = [];
|
||||
this._line = line;
|
||||
newDecoration.push({
|
||||
options: {
|
||||
glyphMarginClassName: EditPreferenceWidget.GLYPH_MARGIN_CLASS_NAME,
|
||||
glyphMarginHoverMessage: new MarkdownString().appendText(hoverMessage),
|
||||
stickiness: editorCommon.TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges,
|
||||
stickiness: TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges,
|
||||
},
|
||||
range: {
|
||||
startLineNumber: line,
|
||||
@@ -890,7 +883,7 @@ registerThemingParticipant((theme: ITheme, collector: ICssStyleCollector) => {
|
||||
outline-style: solid;
|
||||
border-bottom: none;
|
||||
padding-bottom: 0;
|
||||
outline-offset: 3px;
|
||||
outline-offset: 2px;
|
||||
}
|
||||
|
||||
.settings-tabs-widget > .monaco-action-bar .action-item .action-label:not(.checked):hover {
|
||||
@@ -898,4 +891,4 @@ registerThemingParticipant((theme: ITheme, collector: ICssStyleCollector) => {
|
||||
}
|
||||
`);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user