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:
Karl Burtram
2018-04-04 15:27:51 -07:00
committed by GitHub
parent 5fba3e31b4
commit dafb780987
9412 changed files with 141255 additions and 98813 deletions

View File

@@ -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 {

View File

@@ -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)

View File

@@ -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);
}

View File

@@ -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;
}
}

View File

@@ -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 {
});
}
}
}

View File

@@ -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()));

View File

@@ -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);

View File

@@ -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) => {
}
`);
}
});
});