Refresh master with initial release/0.24 snapshot (#332)

* Initial port of release/0.24 source code

* Fix additional headers

* Fix a typo in launch.json
This commit is contained in:
Karl Burtram
2017-12-15 15:38:57 -08:00
committed by GitHub
parent 271b3a0b82
commit 6ad0df0e3e
7118 changed files with 107999 additions and 56466 deletions

View File

@@ -251,6 +251,10 @@ export class KeybindingsEditor extends BaseEditor implements IKeybindingsEditor
this.searchWidget.focus();
}
clearSearchResults(): void {
this.searchWidget.clear();
}
showConflicts(keybindingEntry: IKeybindingItemEntry): TPromise<any> {
const value = `"${keybindingEntry.keybindingItem.keybinding.getAriaLabel()}"`;
if (value !== this.searchWidget.getValue()) {
@@ -517,6 +521,12 @@ export class KeybindingsEditor extends BaseEditor implements IKeybindingsEditor
emptyFilters: this.getLatestEmptyFiltersForTelemetry()
};
this.latestEmptyFilters = [];
/* __GDPR__
"keybindings.filter" : {
"filter": { "classification": "CustomerContent", "purpose": "FeatureInsight" },
"emptyFilters" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
}
*/
this.telemetryService.publicLog('keybindings.filter', data);
}
}
@@ -531,6 +541,7 @@ export class KeybindingsEditor extends BaseEditor implements IKeybindingsEditor
}
private reportKeybindingAction(action: string, command: string, keybinding: ResolvedKeybinding | string): void {
// __GDPR__TODO__ Need to move off dynamic event names and properties as they cannot be registered statically
this.telemetryService.publicLog(action, { command, keybinding: keybinding ? (typeof keybinding === 'string' ? keybinding : keybinding.getUserSettingsLabel()) : '' });
}
@@ -712,16 +723,21 @@ class CommandColumn extends Column {
const commandDefaultLabelMatched = !!keybindingItemEntry.commandDefaultLabelMatches;
DOM.toggleClass(this.commandColumn, 'vertical-align-column', commandIdMatched || commandDefaultLabelMatched);
this.commandColumn.setAttribute('aria-label', this.getAriaLabel(keybindingItemEntry));
let commandLabel: HighlightedLabel;
if (keybindingItem.commandLabel) {
const commandLabel = new HighlightedLabel(this.commandColumn);
commandLabel = new HighlightedLabel(this.commandColumn);
commandLabel.set(keybindingItem.commandLabel, keybindingItemEntry.commandLabelMatches);
commandLabel.element.title = keybindingItem.command;
}
if (keybindingItemEntry.commandDefaultLabelMatches) {
new HighlightedLabel(DOM.append(this.commandColumn, $('.command-default-label'))).set(keybindingItem.commandDefaultLabel, keybindingItemEntry.commandDefaultLabelMatches);
commandLabel = new HighlightedLabel(DOM.append(this.commandColumn, $('.command-default-label')));
commandLabel.set(keybindingItem.commandDefaultLabel, keybindingItemEntry.commandDefaultLabelMatches);
}
if (keybindingItemEntry.commandIdMatches || !keybindingItem.commandLabel) {
new HighlightedLabel(DOM.append(this.commandColumn, $('.code'))).set(keybindingItem.command, keybindingItemEntry.commandIdMatches);
commandLabel = new HighlightedLabel(DOM.append(this.commandColumn, $('.code')));
commandLabel.set(keybindingItem.command, keybindingItemEntry.commandIdMatches);
}
if (commandLabel) {
commandLabel.element.title = keybindingItem.command;
}
}

View File

@@ -73,18 +73,38 @@
padding-right: 32px;
}
.settings-header-widget > .settings-count-widget {
.settings-header-widget > .settings-search-controls > .settings-count-widget {
margin: 6px 0px;
padding: 0px 8px;
position: absolute;
right: 10px;
border-radius: 2px;
float: left;
}
.settings-header-widget > .settings-count-widget.hide {
.settings-header-widget > .settings-search-controls {
position: absolute;
right: 10px;
}
.settings-header-widget > .settings-search-controls > .settings-count-widget.hide {
display: none;
}
.settings-header-widget > .settings-search-controls > .prefs-fuzzy-search-toggle {
margin: 5px 3px 5px 0px;
}
.settings-header-widget > .settings-search-controls > .prefs-fuzzy-search-toggle.hidden {
display: none;
}
.vs .settings-header-widget > .settings-search-controls > .prefs-fuzzy-search-toggle {
background: url('regex.svg') center center no-repeat;
}
.vs-dark .settings-header-widget > .settings-search-controls > .prefs-fuzzy-search-toggle {
background: url('regex-dark.svg') center center no-repeat;
}
.settings-header-widget > .settings-search-container {
flex: 1;
}
@@ -106,8 +126,13 @@
padding-left:10px;
}
.monaco-editor .view-zones > .settings-header-widget {
z-index: 1;
}
.monaco-editor .settings-header-widget .title-container {
display: flex;
user-select: none;
}
.vs .monaco-editor .settings-header-widget .title-container {
@@ -130,6 +155,16 @@
white-space: nowrap;
}
.monaco-editor .settings-header-widget .title-container .settings-header-fuzzy-link {
margin-left: 4px;
text-decoration: underline;
cursor: pointer;
}
.monaco-editor .settings-header-widget .title-container .settings-header-fuzzy-link.hidden {
display: none;
}
.monaco-editor .settings-group-title-widget {
z-index: 1;
}

View File

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

After

Width:  |  Height:  |  Size: 564 B

View File

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

After

Width:  |  Height:  |  Size: 564 B

View File

@@ -8,35 +8,36 @@ import * as nls from 'vs/nls';
import URI from 'vs/base/common/uri';
import { Registry } from 'vs/platform/registry/common/platform';
import { KeybindingsRegistry } from 'vs/platform/keybinding/common/keybindingsRegistry';
import { IWorkbenchActionRegistry, Extensions } from 'vs/workbench/common/actionRegistry';
import { EditorInput, IEditorRegistry, Extensions as EditorExtensions, IEditorInputFactory } from 'vs/workbench/common/editor';
import { EditorDescriptor } from 'vs/workbench/browser/parts/editor/baseEditor';
import { IWorkbenchActionRegistry, Extensions } from 'vs/workbench/common/actions';
import { EditorInput, IEditorInputFactory, IEditorInputFactoryRegistry, Extensions as EditorInputExtensions } from 'vs/workbench/common/editor';
import { SyncActionDescriptor } from 'vs/platform/actions/common/actions';
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
import { KeyMod, KeyChord, KeyCode } from 'vs/base/common/keyCodes';
import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors';
import { DefaultPreferencesEditorInput, PreferencesEditor, PreferencesEditorInput } from 'vs/workbench/parts/preferences/browser/preferencesEditor';
import { KeybindingsEditor, KeybindingsEditorInput } from 'vs/workbench/parts/preferences/browser/keybindingsEditor';
import { OpenGlobalSettingsAction, OpenGlobalKeybindingsAction, OpenGlobalKeybindingsFileAction, OpenWorkspaceSettingsAction, OpenFolderSettingsAction, ConfigureLanguageBasedSettingsAction } from 'vs/workbench/parts/preferences/browser/preferencesActions';
import { OpenGlobalSettingsAction, OpenGlobalKeybindingsAction, OpenGlobalKeybindingsFileAction, OpenWorkspaceSettingsAction, OpenFolderSettingsAction, ConfigureLanguageBasedSettingsAction, OPEN_FOLDER_SETTINGS_COMMAND } from 'vs/workbench/parts/preferences/browser/preferencesActions';
import {
IPreferencesService, IKeybindingsEditor, CONTEXT_KEYBINDING_FOCUS, CONTEXT_KEYBINDINGS_EDITOR, CONTEXT_KEYBINDINGS_SEARCH_FOCUS, KEYBINDINGS_EDITOR_COMMAND_DEFINE, KEYBINDINGS_EDITOR_COMMAND_REMOVE, KEYBINDINGS_EDITOR_COMMAND_SEARCH,
KEYBINDINGS_EDITOR_COMMAND_COPY, KEYBINDINGS_EDITOR_COMMAND_RESET, KEYBINDINGS_EDITOR_COMMAND_SHOW_CONFLICTS, KEYBINDINGS_EDITOR_COMMAND_FOCUS_KEYBINDINGS
KEYBINDINGS_EDITOR_COMMAND_COPY, KEYBINDINGS_EDITOR_COMMAND_RESET, KEYBINDINGS_EDITOR_COMMAND_SHOW_CONFLICTS, KEYBINDINGS_EDITOR_COMMAND_FOCUS_KEYBINDINGS, KEYBINDINGS_EDITOR_COMMAND_CLEAR_SEARCH_RESULTS
} from 'vs/workbench/parts/preferences/common/preferences';
import { PreferencesService } from 'vs/workbench/parts/preferences/browser/preferencesService';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } from 'vs/workbench/common/contributions';
import { PreferencesContentProvider } from 'vs/workbench/parts/preferences/common/preferencesContentProvider';
import { PreferencesContribution } from 'vs/workbench/parts/preferences/common/preferencesContribution';
import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService';
import { CommandsRegistry } from 'vs/platform/commands/common/commands';
import { IWorkspaceFolder } from 'vs/platform/workspace/common/workspace';
import { IEditorRegistry, EditorDescriptor, Extensions as EditorExtensions } from 'vs/workbench/browser/editor';
registerSingleton(IPreferencesService, PreferencesService);
Registry.as<IEditorRegistry>(EditorExtensions.Editors).registerEditor(
new EditorDescriptor(
PreferencesEditor,
PreferencesEditor.ID,
nls.localize('defaultPreferencesEditor', "Default Preferences Editor"),
'vs/workbench/parts/preferences/browser/preferencesEditor',
'PreferencesEditor'
nls.localize('defaultPreferencesEditor', "Default Preferences Editor")
),
[
new SyncDescriptor(PreferencesEditorInput)
@@ -45,10 +46,9 @@ Registry.as<IEditorRegistry>(EditorExtensions.Editors).registerEditor(
Registry.as<IEditorRegistry>(EditorExtensions.Editors).registerEditor(
new EditorDescriptor(
KeybindingsEditor,
KeybindingsEditor.ID,
nls.localize('keybindingsEditor', "Keybindings Editor"),
'vs/workbench/parts/preferences/browser/keybindingsEditor',
'KeybindingsEditor'
nls.localize('keybindingsEditor', "Keybindings Editor")
),
[
new SyncDescriptor(KeybindingsEditorInput)
@@ -73,7 +73,7 @@ class PreferencesEditorInputFactory implements IEditorInputFactory {
const input = <PreferencesEditorInput>editorInput;
if (input.details && input.master) {
const registry = Registry.as<IEditorRegistry>(EditorExtensions.Editors);
const registry = Registry.as<IEditorInputFactoryRegistry>(EditorInputExtensions.EditorInputFactories);
const detailsInputFactory = registry.getEditorInputFactory(input.details.getTypeId());
const masterInputFactory = registry.getEditorInputFactory(input.master.getTypeId());
@@ -100,7 +100,7 @@ class PreferencesEditorInputFactory implements IEditorInputFactory {
public deserialize(instantiationService: IInstantiationService, serializedEditorInput: string): EditorInput {
const deserialized: ISerializedPreferencesEditorInput = JSON.parse(serializedEditorInput);
const registry = Registry.as<IEditorRegistry>(EditorExtensions.Editors);
const registry = Registry.as<IEditorInputFactoryRegistry>(EditorInputExtensions.EditorInputFactories);
const detailsInputFactory = registry.getEditorInputFactory(deserialized.detailsTypeId);
const masterInputFactory = registry.getEditorInputFactory(deserialized.masterTypeId);
@@ -155,9 +155,9 @@ class DefaultPreferencesEditorInputFactory implements IEditorInputFactory {
}
}
Registry.as<IEditorRegistry>(EditorExtensions.Editors).registerEditorInputFactory(PreferencesEditorInput.ID, PreferencesEditorInputFactory);
Registry.as<IEditorRegistry>(EditorExtensions.Editors).registerEditorInputFactory(DefaultPreferencesEditorInput.ID, DefaultPreferencesEditorInputFactory);
Registry.as<IEditorRegistry>(EditorExtensions.Editors).registerEditorInputFactory(KeybindingsEditorInput.ID, KeybindingsEditorInputFactory);
Registry.as<IEditorInputFactoryRegistry>(EditorInputExtensions.EditorInputFactories).registerEditorInputFactory(PreferencesEditorInput.ID, PreferencesEditorInputFactory);
Registry.as<IEditorInputFactoryRegistry>(EditorInputExtensions.EditorInputFactories).registerEditorInputFactory(DefaultPreferencesEditorInput.ID, DefaultPreferencesEditorInputFactory);
Registry.as<IEditorInputFactoryRegistry>(EditorInputExtensions.EditorInputFactories).registerEditorInputFactory(KeybindingsEditorInput.ID, KeybindingsEditorInputFactory);
// Contribute Global Actions
const category = nls.localize('preferences', "Preferences");
@@ -246,4 +246,20 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({
}
});
Registry.as<IWorkbenchContributionsRegistry>(WorkbenchExtensions.Workbench).registerWorkbenchContribution(PreferencesContentProvider);
KeybindingsRegistry.registerCommandAndKeybindingRule({
id: KEYBINDINGS_EDITOR_COMMAND_CLEAR_SEARCH_RESULTS,
weight: KeybindingsRegistry.WEIGHT.workbenchContrib(),
when: ContextKeyExpr.and(CONTEXT_KEYBINDINGS_EDITOR, CONTEXT_KEYBINDINGS_SEARCH_FOCUS),
primary: KeyCode.Escape,
handler: (accessor, args: any) => {
const editor = accessor.get(IWorkbenchEditorService).getActiveEditor() as IKeybindingsEditor;
editor.clearSearchResults();
}
});
Registry.as<IWorkbenchContributionsRegistry>(WorkbenchExtensions.Workbench).registerWorkbenchContribution(PreferencesContribution);
CommandsRegistry.registerCommand(OPEN_FOLDER_SETTINGS_COMMAND, function (accessor: ServicesAccessor, args?: IWorkspaceFolder) {
const preferencesService = accessor.get(IPreferencesService);
return preferencesService.openFolderSettings(args.uri);
});

View File

@@ -8,11 +8,13 @@ import { TPromise } from 'vs/base/common/winjs.base';
import * as nls from 'vs/nls';
import URI from 'vs/base/common/uri';
import { Action } from 'vs/base/common/actions';
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import { IModeService } from 'vs/editor/common/services/modeService';
import { IQuickOpenService, IPickOpenEntry, IFilePickOpenEntry } from 'vs/platform/quickOpen/common/quickOpen';
import { IPreferencesService, getSettingsTargetName } from 'vs/workbench/parts/preferences/common/preferences';
import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
import { ConfigurationTarget } from 'vs/workbench/services/configuration/common/configurationEditing';
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';
export class OpenGlobalSettingsAction extends Action {
@@ -73,6 +75,8 @@ export class OpenWorkspaceSettingsAction extends Action {
public static ID = 'workbench.action.openWorkspaceSettings';
public static LABEL = nls.localize('openWorkspaceSettings', "Open Workspace Settings");
private disposables: IDisposable[] = [];
constructor(
id: string,
label: string,
@@ -80,46 +84,63 @@ export class OpenWorkspaceSettingsAction extends Action {
@IWorkspaceContextService private workspaceContextService: IWorkspaceContextService
) {
super(id, label);
this.enabled = this.workspaceContextService.hasWorkspace();
this.update();
this.workspaceContextService.onDidChangeWorkbenchState(() => this.update(), this, this.disposables);
}
private update(): void {
this.enabled = this.workspaceContextService.getWorkbenchState() !== WorkbenchState.EMPTY;
}
public run(event?: any): TPromise<any> {
return this.preferencesService.openWorkspaceSettings();
}
public dispose(): void {
this.disposables = dispose(this.disposables);
super.dispose();
}
}
export const OPEN_FOLDER_SETTINGS_COMMAND = '_workbench.action.openFolderSettings';
export class OpenFolderSettingsAction extends Action {
public static ID = 'workbench.action.openFolderSettings';
public static LABEL = nls.localize('openFolderSettings', "Open Folder Settings");
private disposables: IDisposable[] = [];
constructor(
id: string,
label: string,
@IPreferencesService private preferencesService: IPreferencesService,
@IWorkspaceContextService private workspaceContextService: IWorkspaceContextService,
@IQuickOpenService private quickOpenService: IQuickOpenService
@ICommandService private commandService: ICommandService
) {
super(id, label);
this.enabled = this.workspaceContextService.hasMultiFolderWorkspace();
this.update();
this.workspaceContextService.onDidChangeWorkbenchState(() => this.update(), this, this.disposables);
this.workspaceContextService.onDidChangeWorkspaceFolders(() => this.update(), this, this.disposables);
}
private update(): void {
this.enabled = this.workspaceContextService.getWorkbenchState() === WorkbenchState.WORKSPACE && this.workspaceContextService.getWorkspace().folders.length > 0;
}
public run(): TPromise<any> {
const picks: IPickOpenEntry[] = this.workspaceContextService.getWorkspace().roots.map((root, index) => {
return <IPickOpenEntry>{
label: getSettingsTargetName(ConfigurationTarget.FOLDER, root, this.workspaceContextService),
id: `${index}`
};
});
return this.quickOpenService.pick(picks, { placeHolder: nls.localize('pickFolder', "Select Folder") })
.then(pick => {
if (pick) {
return this.preferencesService.openFolderSettings(this.workspaceContextService.getWorkspace().roots[parseInt(pick.id)]);
return this.commandService.executeCommand<IWorkspaceFolder>(PICK_WORKSPACE_FOLDER_COMMAND)
.then(workspaceFolder => {
if (workspaceFolder) {
return this.commandService.executeCommand(OPEN_FOLDER_SETTINGS_COMMAND, workspaceFolder);
}
return undefined;
return null;
});
}
public dispose(): void {
this.disposables = dispose(this.disposables);
super.dispose();
}
}

View File

@@ -6,14 +6,15 @@
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 DOM from 'vs/base/browser/dom';
import { Delayer } from 'vs/base/common/async';
import { Delayer, ThrottledDelayer } from 'vs/base/common/async';
import { Dimension, Builder } from 'vs/base/browser/builder';
import { ArrayNavigator, INavigator } from 'vs/base/common/iterator';
import { Disposable, IDisposable, dispose } from 'vs/base/common/lifecycle';
import { KeyMod, KeyCode } from 'vs/base/common/keyCodes';
import { toResource, SideBySideEditorInput, EditorOptions, EditorInput, IEditorRegistry, Extensions as EditorExtensions } from 'vs/workbench/common/editor';
import { BaseEditor, EditorDescriptor } from 'vs/workbench/browser/parts/editor/baseEditor';
import { SideBySideEditorInput, EditorOptions, EditorInput } 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';
import { ResourceEditorInput } from 'vs/workbench/common/editor/resourceEditorInput';
@@ -23,12 +24,13 @@ import { CodeEditor } from 'vs/editor/browser/codeEditor';
import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
import {
IPreferencesService, ISettingsGroup, ISetting, IFilterResult,
CONTEXT_SETTINGS_EDITOR, CONTEXT_SETTINGS_SEARCH_FOCUS, SETTINGS_EDITOR_COMMAND_SEARCH, SETTINGS_EDITOR_COMMAND_FOCUS_FILE, ISettingsEditorModel
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
} from 'vs/workbench/parts/preferences/common/preferences';
import { SettingsEditorModel, DefaultSettingsEditorModel } from 'vs/workbench/parts/preferences/common/preferencesModels';
import { editorContribution } from 'vs/editor/browser/editorBrowserExtensions';
import { ICodeEditor, IEditorContributionCtor } from 'vs/editor/browser/editorBrowser';
import { SearchWidget, SettingsTargetsWidget } from 'vs/workbench/parts/preferences/browser/preferencesWidgets';
import { PreferencesSearchProvider, PreferencesSearchModel } from 'vs/workbench/parts/preferences/browser/preferencesSearch';
import { ContextKeyExpr, IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey';
import { Command } from 'vs/editor/common/editorCommonExtensions';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
@@ -39,7 +41,6 @@ import { IStorageService } from 'vs/platform/storage/common/storage';
import { ITextResourceConfigurationService } from 'vs/editor/common/services/resourceConfiguration';
import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService';
import { ITextModelService } from 'vs/editor/common/services/resolverService';
import { ConfigurationTarget } from 'vs/workbench/services/configuration/common/configurationEditing';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { VSash } from 'vs/base/browser/ui/sash/sash';
import { Widget } from 'vs/base/browser/ui/widget';
@@ -47,11 +48,10 @@ import { IPreferencesRenderer, DefaultSettingsRenderer, UserSettingsRenderer, Wo
import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles';
import { IEditorGroupService } from 'vs/workbench/services/group/common/groupService';
import { getCodeEditor } from 'vs/editor/common/services/codeEditorService';
// Ignore following contributions
import { IEditorRegistry, Extensions as EditorExtensions } from 'vs/workbench/browser/editor';
import { FoldingController } from 'vs/editor/contrib/folding/browser/folding';
import { FindController } from 'vs/editor/contrib/find/browser/find';
import { SelectionHighlighter } from 'vs/editor/contrib/find/common/findController';
import { SelectionHighlighter } from 'vs/editor/contrib/multicursor/common/multicursor';
import { IEditorOptions } from 'vs/editor/common/config/editorOptions';
import { KeybindingsRegistry } from 'vs/platform/keybinding/common/keybindingsRegistry';
import { attachStylerCallback } from 'vs/platform/theme/common/styler';
@@ -59,6 +59,9 @@ import { scrollbarShadow } from 'vs/platform/theme/common/colorRegistry';
import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
import Event, { Emitter } from 'vs/base/common/event';
import { Registry } from 'vs/platform/registry/common/platform';
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';
export class PreferencesEditorInput extends SideBySideEditorInput {
public static ID: string = 'workbench.editorinputs.preferencesEditorInput';
@@ -75,9 +78,10 @@ export class PreferencesEditorInput extends SideBySideEditorInput {
export class DefaultPreferencesEditorInput extends ResourceEditorInput {
public static ID = 'workbench.editorinputs.defaultpreferences';
constructor(defaultSettingsResource: URI,
@ITextModelService textModelResolverService: ITextModelService
@ITextModelService textModelResolverService: ITextModelService,
@IHashService hashService: IHashService
) {
super(nls.localize('settingsEditorName', "Default Settings"), '', defaultSettingsResource, textModelResolverService);
super(nls.localize('settingsEditorName', "Default Settings"), '', defaultSettingsResource, textModelResolverService, hashService);
}
getTypeId(): string {
@@ -85,10 +89,10 @@ export class DefaultPreferencesEditorInput extends ResourceEditorInput {
}
matches(other: any): boolean {
if (!super.matches(other)) {
return false;
if (other instanceof DefaultPreferencesEditorInput) {
return true;
}
if (!(other instanceof DefaultPreferencesEditorInput)) {
if (!super.matches(other)) {
return false;
}
return true;
@@ -106,8 +110,10 @@ export class PreferencesEditor extends BaseEditor {
private settingsTargetsWidget: SettingsTargetsWidget;
private sideBySidePreferencesWidget: SideBySidePreferencesWidget;
private preferencesRenderers: PreferencesRenderers;
private searchProvider: PreferencesSearchProvider;
private delayedFilterLogging: Delayer<void>;
private filterThrottle: ThrottledDelayer<void>;
private latestEmptyFilters: string[] = [];
private lastFocusedWidget: SearchWidget | SideBySidePreferencesWidget = null;
@@ -126,6 +132,8 @@ export class PreferencesEditor extends BaseEditor {
this.defaultSettingsEditorContextKey = CONTEXT_SETTINGS_EDITOR.bindTo(this.contextKeyService);
this.focusSettingsContextKey = CONTEXT_SETTINGS_SEARCH_FOCUS.bindTo(this.contextKeyService);
this.delayedFilterLogging = new Delayer<void>(1000);
this.searchProvider = this.instantiationService.createInstance(PreferencesSearchProvider);
this.filterThrottle = new ThrottledDelayer(200);
}
public createEditor(parent: Builder): void {
@@ -139,8 +147,9 @@ export class PreferencesEditor extends BaseEditor {
placeholder: nls.localize('SearchSettingsWidget.Placeholder', "Search Settings"),
focusKey: this.focusSettingsContextKey
}));
this._register(this.searchWidget.onDidChange(value => this.filterPreferences(value.trim())));
this._register(this.searchWidget.onNavigate(shift => this.preferencesRenderers.focusNextPreference(!shift)));
this.searchWidget.setFuzzyToggleVisible(this.searchProvider.remoteSearchEnabled);
this._register(this.searchProvider.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;
@@ -152,7 +161,31 @@ export class PreferencesEditor extends BaseEditor {
this._register(this.sideBySidePreferencesWidget.onFocus(() => this.lastFocusedWidget = this.sideBySidePreferencesWidget));
this.preferencesRenderers = this._register(new PreferencesRenderers());
this._register(this.workspaceContextService.onDidChangeWorkspaceRoots(() => this.onWorkspaceRootsChanged()));
this._register(this.workspaceContextService.onDidChangeWorkspaceFolders(() => this.onWorkspaceFoldersChanged()));
this._register(this.workspaceContextService.onDidChangeWorkbenchState(() => this.onWorkbenchStateChanged()));
this._register(this.preferencesRenderers.onTriggeredFuzzy(() => {
this.searchWidget.fuzzyEnabled = true;
this.filterPreferences();
}));
}
public clearSearchResults(): void {
if (this.searchWidget) {
this.searchWidget.clear();
}
}
public focusNextResult(): void {
if (this.preferencesRenderers) {
this.preferencesRenderers.focusNextPreference(true);
}
}
public focusPreviousResult(): void {
if (this.preferencesRenderers) {
this.preferencesRenderers.focusNextPreference(false);
}
}
public setInput(newInput: PreferencesEditorInput, options?: EditorOptions): TPromise<void> {
@@ -209,43 +242,60 @@ export class PreferencesEditor extends BaseEditor {
}
private updateInput(oldInput: PreferencesEditorInput, newInput: PreferencesEditorInput, options?: EditorOptions): TPromise<void> {
const resource = toResource(newInput.master);
this.settingsTargetsWidget.setTarget(this.getSettingsConfigurationTargetUri(resource), this.getSettingsConfigurationTarget(resource));
const resource = newInput.master.getResource();
this.settingsTargetsWidget.updateTargets(this.getSettingsConfigurationTargetUri(resource), this.getSettingsConfigurationTarget(resource));
return this.sideBySidePreferencesWidget.setInput(<DefaultPreferencesEditorInput>newInput.details, <EditorInput>newInput.master, options).then(({ defaultPreferencesRenderer, editablePreferencesRenderer }) => {
this.preferencesRenderers.defaultPreferencesRenderer = defaultPreferencesRenderer;
this.preferencesRenderers.editablePreferencesRenderer = editablePreferencesRenderer;
this.filterPreferences(this.searchWidget.getValue());
this.onInputChanged();
});
}
private onInputChanged(): void {
if (this.searchWidget.fuzzyEnabled) {
this.triggerThrottledFilter();
} else {
this.filterPreferences();
}
}
private triggerThrottledFilter(): void {
this.filterThrottle.trigger(() => this.filterPreferences());
}
private getSettingsConfigurationTarget(resource: URI): ConfigurationTarget {
if (this.preferencesService.userSettingsResource.fsPath === resource.fsPath) {
if (this.preferencesService.userSettingsResource.toString() === resource.toString()) {
return ConfigurationTarget.USER;
}
if (this.preferencesService.workspaceSettingsResource.fsPath === resource.fsPath) {
const workspaceSettingsResource = this.preferencesService.workspaceSettingsResource;
if (workspaceSettingsResource && workspaceSettingsResource.toString() === resource.toString()) {
return ConfigurationTarget.WORKSPACE;
}
if (this.workspaceContextService.getRoot(resource)) {
return ConfigurationTarget.FOLDER;
if (this.workspaceContextService.getWorkspaceFolder(resource)) {
return ConfigurationTarget.WORKSPACE_FOLDER;
}
return null;
}
private getSettingsConfigurationTargetUri(resource: URI): URI {
if (this.preferencesService.userSettingsResource.fsPath === resource.fsPath) {
if (this.preferencesService.userSettingsResource.toString() === resource.toString()) {
return resource;
}
if (this.preferencesService.workspaceSettingsResource.fsPath === resource.fsPath) {
if (this.preferencesService.workspaceSettingsResource.toString() === resource.toString()) {
return resource;
}
return this.workspaceContextService.getRoot(resource);
const workspaceFolder = this.workspaceContextService.getWorkspaceFolder(resource);
return workspaceFolder ? workspaceFolder.uri : null;
}
private onWorkspaceRootsChanged(): void {
private onWorkspaceFoldersChanged(): void {
if (this.input) {
const settingsResource = toResource((<PreferencesEditorInput>this.input).master);
const settingsResource = (<PreferencesEditorInput>this.input).master.getResource();
const targetResource = this.getSettingsConfigurationTargetUri(settingsResource);
if (!targetResource) {
this.switchSettings(this.preferencesService.userSettingsResource);
@@ -253,6 +303,21 @@ export class PreferencesEditor extends BaseEditor {
}
}
private onWorkbenchStateChanged(): void {
if (this.input) {
const editableSettingsResource = (<PreferencesEditorInput>this.input).master.getResource();
const newConfigurationTarget = this.getSettingsConfigurationTarget(editableSettingsResource);
if (newConfigurationTarget) {
if (newConfigurationTarget !== this.settingsTargetsWidget.configurationTarget) {
// Update the editor if the configuration target of the settings resource changed
this.switchSettings(editableSettingsResource);
}
} else {
this.switchSettings(this.preferencesService.userSettingsResource);
}
}
}
private switchSettings(resource: URI): void {
// Focus the editor if this editor is not active editor
if (this.editorService.getActiveEditor() !== this) {
@@ -262,14 +327,16 @@ export class PreferencesEditor extends BaseEditor {
promise.done(value => this.preferencesService.switchSettings(this.getSettingsConfigurationTarget(resource), resource));
}
private filterPreferences(filter: string) {
const count = this.preferencesRenderers.filterPreferences(filter);
const message = filter ? this.showSearchResultsMessage(count) : nls.localize('totalSettingsMessage', "Total {0} Settings", count);
this.searchWidget.showMessage(message, count);
if (count === 0) {
this.latestEmptyFilters.push(filter);
}
this.delayedFilterLogging.trigger(() => this.reportFilteringUsed(filter));
private filterPreferences(): TPromise<void> {
const filter = this.searchWidget.getValue().trim();
return this.preferencesRenderers.filterPreferences(filter, this.searchProvider, this.searchWidget.fuzzyEnabled).then(count => {
const message = filter ? this.showSearchResultsMessage(count) : nls.localize('totalSettingsMessage', "Total {0} Settings", count);
this.searchWidget.showMessage(message, count);
if (count === 0) {
this.latestEmptyFilters.push(filter);
}
this.delayedFilterLogging.trigger(() => this.reportFilteringUsed(filter));
}, onUnexpectedError);
}
private showSearchResultsMessage(count: number): string {
@@ -285,6 +352,12 @@ export class PreferencesEditor extends BaseEditor {
emptyFilters: this.getLatestEmptyFiltersForTelemetry()
};
this.latestEmptyFilters = [];
/* __GDPR__
"defaultSettings.filter" : {
"filter": { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
"emptyFilters" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
}
*/
this.telemetryService.publicLog('defaultSettings.filter', data);
}
}
@@ -337,9 +410,13 @@ class PreferencesRenderers extends Disposable {
private _defaultPreferencesRenderer: IPreferencesRenderer<ISetting>;
private _editablePreferencesRenderer: IPreferencesRenderer<ISetting>;
private _settingsNavigator: SettingsNavigator;
private _filtersInProgress: TPromise<any>[];
private _disposables: IDisposable[] = [];
private _onTriggeredFuzzy: Emitter<void> = new Emitter<void>();
public onTriggeredFuzzy: Event<void> = this._onTriggeredFuzzy.event;
public get defaultPreferencesRenderer(): IPreferencesRenderer<ISetting> {
return this._defaultPreferencesRenderer;
}
@@ -354,6 +431,9 @@ class PreferencesRenderers extends Disposable {
this._defaultPreferencesRenderer.onUpdatePreference(({ key, value, source }) => this._updatePreference(key, value, source, this._editablePreferencesRenderer), this, this._disposables);
this._defaultPreferencesRenderer.onFocusPreference(preference => this._focusPreference(preference, this._editablePreferencesRenderer), this, this._disposables);
this._defaultPreferencesRenderer.onClearFocusPreference(preference => this._clearFocus(preference, this._editablePreferencesRenderer), this, this._disposables);
if (this._defaultPreferencesRenderer.onTriggeredFuzzy) {
this._register(this._defaultPreferencesRenderer.onTriggeredFuzzy(() => this._onTriggeredFuzzy.fire()));
}
}
}
}
@@ -362,19 +442,37 @@ class PreferencesRenderers extends Disposable {
this._editablePreferencesRenderer = editableSettingsRenderer;
}
public filterPreferences(filter: string): number {
const defaultPreferencesFilterResult = this._filterPreferences(filter, this._defaultPreferencesRenderer);
const editablePreferencesFilterResult = this._filterPreferences(filter, this._editablePreferencesRenderer);
public filterPreferences(filter: string, searchProvider: PreferencesSearchProvider, fuzzy: boolean): TPromise<number> {
if (this._filtersInProgress) {
// Resolved/rejected promises have no .cancel()
this._filtersInProgress.forEach(p => p.cancel && p.cancel());
}
const defaultPreferencesFilteredGroups = defaultPreferencesFilterResult ? defaultPreferencesFilterResult.filteredGroups : this._getAllPreferences(this._defaultPreferencesRenderer);
const editablePreferencesFilteredGroups = editablePreferencesFilterResult ? editablePreferencesFilterResult.filteredGroups : this._getAllPreferences(this._editablePreferencesRenderer);
const consolidatedSettings = this._consolidateSettings(editablePreferencesFilteredGroups, defaultPreferencesFilteredGroups);
this._settingsNavigator = new SettingsNavigator(filter ? consolidatedSettings : []);
const searchModel = searchProvider.startSearch(filter, fuzzy);
this._filtersInProgress = [
this._filterPreferences(searchModel, searchProvider, this._defaultPreferencesRenderer),
this._filterPreferences(searchModel, searchProvider, this._editablePreferencesRenderer)];
return consolidatedSettings.length;
return TPromise.join<IFilterResult>(this._filtersInProgress).then(filterResults => {
this._filtersInProgress = null;
const defaultPreferencesFilterResult = filterResults[0];
const editablePreferencesFilterResult = filterResults[1];
const defaultPreferencesFilteredGroups = defaultPreferencesFilterResult ? defaultPreferencesFilterResult.filteredGroups : this._getAllPreferences(this._defaultPreferencesRenderer);
const editablePreferencesFilteredGroups = editablePreferencesFilterResult ? editablePreferencesFilterResult.filteredGroups : this._getAllPreferences(this._editablePreferencesRenderer);
const consolidatedSettings = this._consolidateSettings(editablePreferencesFilteredGroups, defaultPreferencesFilteredGroups);
this._settingsNavigator = new SettingsNavigator(filter ? consolidatedSettings : []);
return consolidatedSettings.length;
});
}
public focusNextPreference(forward: boolean = true) {
if (!this._settingsNavigator) {
return;
}
const setting = forward ? this._settingsNavigator.next() : this._settingsNavigator.previous();
this._focusPreference(setting, this._defaultPreferencesRenderer);
this._focusPreference(setting, this._editablePreferencesRenderer);
@@ -384,13 +482,17 @@ class PreferencesRenderers extends Disposable {
return preferencesRenderer ? (<ISettingsEditorModel>preferencesRenderer.preferencesModel).settingsGroups : [];
}
private _filterPreferences(filter: string, preferencesRenderer: IPreferencesRenderer<ISetting>): IFilterResult {
let filterResult = null;
private _filterPreferences(searchModel: PreferencesSearchModel, searchProvider: PreferencesSearchProvider, preferencesRenderer: IPreferencesRenderer<ISetting>): TPromise<IFilterResult> {
if (preferencesRenderer) {
filterResult = filter ? (<ISettingsEditorModel>preferencesRenderer.preferencesModel).filterSettings(filter) : null;
preferencesRenderer.filterPreferences(filterResult);
const prefSearchP = searchModel.filterPreferences(<ISettingsEditorModel>preferencesRenderer.preferencesModel);
return prefSearchP.then(filterResult => {
preferencesRenderer.filterPreferences(filterResult, searchProvider.remoteSearchEnabled);
return filterResult;
});
}
return filterResult;
return TPromise.wrap(null);
}
private _focusPreference(preference: ISetting, preferencesRenderer: IPreferencesRenderer<ISetting>): void {
@@ -483,13 +585,11 @@ class SideBySidePreferencesWidget extends Widget {
}
public setInput(defaultPreferencesEditorInput: DefaultPreferencesEditorInput, editablePreferencesEditorInput: EditorInput, options?: EditorOptions): TPromise<{ defaultPreferencesRenderer: IPreferencesRenderer<ISetting>, editablePreferencesRenderer: IPreferencesRenderer<ISetting> }> {
return this.getOrCreateEditablePreferencesEditor(editablePreferencesEditorInput)
.then(() => {
this.dolayout(this.sash.getVerticalSashLeft());
return TPromise.join([this.updateInput(this.defaultPreferencesEditor, defaultPreferencesEditorInput, DefaultSettingsEditorContribution.ID, toResource(editablePreferencesEditorInput), options),
this.updateInput(this.editablePreferencesEditor, editablePreferencesEditorInput, SettingsEditorContribution.ID, defaultPreferencesEditorInput.getResource(), options)])
.then(([defaultPreferencesRenderer, editablePreferencesRenderer]) => ({ defaultPreferencesRenderer, editablePreferencesRenderer }));
});
this.getOrCreateEditablePreferencesEditor(editablePreferencesEditorInput);
this.dolayout(this.sash.getVerticalSashLeft());
return TPromise.join([this.updateInput(this.defaultPreferencesEditor, defaultPreferencesEditorInput, DefaultSettingsEditorContribution.ID, editablePreferencesEditorInput.getResource(), options),
this.updateInput(this.editablePreferencesEditor, editablePreferencesEditorInput, SettingsEditorContribution.ID, defaultPreferencesEditorInput.getResource(), options)])
.then(([defaultPreferencesRenderer, editablePreferencesRenderer]) => ({ defaultPreferencesRenderer, editablePreferencesRenderer }));
}
public layout(dimension: Dimension): void {
@@ -508,6 +608,9 @@ class SideBySidePreferencesWidget extends Widget {
}
public clearInput(): void {
if (this.defaultPreferencesEditor) {
this.defaultPreferencesEditor.clearInput();
}
if (this.editablePreferencesEditor) {
this.editablePreferencesEditor.clearInput();
}
@@ -525,20 +628,19 @@ class SideBySidePreferencesWidget extends Widget {
}
}
private getOrCreateEditablePreferencesEditor(editorInput: EditorInput): TPromise<BaseEditor> {
private getOrCreateEditablePreferencesEditor(editorInput: EditorInput): BaseEditor {
if (this.editablePreferencesEditor) {
return TPromise.as(this.editablePreferencesEditor);
return this.editablePreferencesEditor;
}
const descriptor = Registry.as<IEditorRegistry>(EditorExtensions.Editors).getEditor(editorInput);
return this.instantiationService.createInstance(<EditorDescriptor>descriptor)
.then((editor: BaseEditor) => {
this.editablePreferencesEditor = editor;
this.editablePreferencesEditor.create(new Builder(this.editablePreferencesEditorContainer));
this.editablePreferencesEditor.setVisible(true);
(<CodeEditor>this.editablePreferencesEditor.getControl()).onDidFocusEditor(() => this.lastFocusedEditor = this.editablePreferencesEditor);
this.lastFocusedEditor = this.editablePreferencesEditor;
return editor;
});
const editor = descriptor.instantiate(this.instantiationService);
this.editablePreferencesEditor = editor;
this.editablePreferencesEditor.create(new Builder(this.editablePreferencesEditorContainer));
this.editablePreferencesEditor.setVisible(true);
(<CodeEditor>this.editablePreferencesEditor.getControl()).onDidFocusEditor(() => this.lastFocusedEditor = this.editablePreferencesEditor);
this.lastFocusedEditor = this.editablePreferencesEditor;
return editor;
}
private updateInput(editor: BaseEditor, input: EditorInput, editorContributionId: string, associatedPreferencesModelUri: URI, options: EditorOptions): TPromise<IPreferencesRenderer<ISetting>> {
@@ -682,7 +784,20 @@ export class DefaultPreferencesEditor extends BaseTextEditor {
}
public createEditorControl(parent: Builder, configuration: IEditorOptions): editorCommon.IEditor {
return this.instantiationService.createInstance(DefaultPreferencesCodeEditor, parent.getHTMLElement(), configuration);
const editor = this.instantiationService.createInstance(DefaultPreferencesCodeEditor, parent.getHTMLElement(), configuration);
// Inform user about editor being readonly if user starts type
this.toUnbind.push(editor.onDidType(() => this.showReadonlyHint(editor)));
this.toUnbind.push(editor.onDidPaste(() => this.showReadonlyHint(editor)));
return editor;
}
private showReadonlyHint(editor: editorCommon.ICommonCodeEditor): void {
const messageController = MessageController.get(editor);
if (!messageController.isVisible()) {
messageController.showMessage(nls.localize('defaultEditorReadonly', "Edit in the right hand side editor to override defaults."), editor.getSelection().getPosition());
}
}
protected getConfigurationOverrides(): IEditorOptions {
@@ -712,6 +827,14 @@ export class DefaultPreferencesEditor extends BaseTextEditor {
.then(editorModel => this.getControl().setModel((<ResourceEditorModel>editorModel).textEditorModel)));
}
public clearInput(): void {
// Clear Model
this.getControl().setModel(null);
// Pass to super
super.clearInput();
}
public layout(dimension: Dimension) {
this.getControl().layout(dimension);
}
@@ -765,7 +888,7 @@ abstract class AbstractSettingsEditorContribution extends Disposable {
return TPromise.as(null);
}
private _onModelChanged(): void {
protected _onModelChanged(): void {
const model = this.editor.getModel();
this.disposePreferencesRenderer();
if (model) {
@@ -775,7 +898,7 @@ abstract class AbstractSettingsEditorContribution extends Disposable {
private _hasAssociatedPreferencesModelChanged(associatedPreferencesModelUri: URI): TPromise<boolean> {
return this.preferencesRendererCreationPromise.then(preferencesRenderer => {
return !(preferencesRenderer && preferencesRenderer.associatedPreferencesModel && preferencesRenderer.associatedPreferencesModel.uri.fsPath === associatedPreferencesModelUri.fsPath);
return !(preferencesRenderer && preferencesRenderer.associatedPreferencesModel && preferencesRenderer.associatedPreferencesModel.uri.toString() === associatedPreferencesModelUri.toString());
});
}
@@ -801,6 +924,7 @@ abstract class AbstractSettingsEditorContribution extends Disposable {
if (preferencesRenderer.associatedPreferencesModel) {
preferencesRenderer.associatedPreferencesModel.dispose();
}
preferencesRenderer.preferencesModel.dispose();
preferencesRenderer.dispose();
}
});
@@ -842,22 +966,31 @@ class SettingsEditorContribution extends AbstractSettingsEditorContribution impl
static ID: string = 'editor.contrib.settings';
constructor(editor: ICodeEditor,
@IInstantiationService instantiationService: IInstantiationService,
@IPreferencesService preferencesService: IPreferencesService,
@IWorkspaceContextService workspaceContextService: IWorkspaceContextService
) {
super(editor, instantiationService, preferencesService, workspaceContextService);
this._register(this.workspaceContextService.onDidChangeWorkbenchState(() => this._onModelChanged()));
}
getId(): string {
return SettingsEditorContribution.ID;
}
protected _createPreferencesRenderer(): TPromise<IPreferencesRenderer<ISetting>> {
if (this.isSettingsModel()) {
return TPromise.join<any>([this.preferencesService.createPreferencesEditorModel(this.preferencesService.defaultSettingsResource), this.preferencesService.createPreferencesEditorModel(this.editor.getModel().uri)])
.then(([defaultSettingsModel, settingsModel]) => {
return this.preferencesService.createPreferencesEditorModel(this.editor.getModel().uri)
.then(settingsModel => {
if (settingsModel instanceof SettingsEditorModel && this.editor.getModel()) {
switch (settingsModel.configurationTarget) {
case ConfigurationTarget.USER:
return this.instantiationService.createInstance(UserSettingsRenderer, this.editor, settingsModel, defaultSettingsModel);
return this.instantiationService.createInstance(UserSettingsRenderer, this.editor, settingsModel);
case ConfigurationTarget.WORKSPACE:
return this.instantiationService.createInstance(WorkspaceSettingsRenderer, this.editor, settingsModel, defaultSettingsModel);
case ConfigurationTarget.FOLDER:
return this.instantiationService.createInstance(FolderSettingsRenderer, this.editor, settingsModel, defaultSettingsModel);
return this.instantiationService.createInstance(WorkspaceSettingsRenderer, this.editor, settingsModel);
case ConfigurationTarget.WORKSPACE_FOLDER:
return this.instantiationService.createInstance(FolderSettingsRenderer, this.editor, settingsModel);
}
}
return null;
@@ -878,21 +1011,18 @@ class SettingsEditorContribution extends AbstractSettingsEditorContribution impl
return false;
}
if (this.preferencesService.userSettingsResource && this.preferencesService.userSettingsResource.fsPath === model.uri.fsPath) {
if (this.preferencesService.userSettingsResource && this.preferencesService.userSettingsResource.toString() === model.uri.toString()) {
return true;
}
if (this.preferencesService.workspaceSettingsResource && this.preferencesService.workspaceSettingsResource.fsPath === model.uri.fsPath) {
if (this.preferencesService.workspaceSettingsResource && this.preferencesService.workspaceSettingsResource.toString() === model.uri.toString()) {
return true;
}
const workspace = this.workspaceContextService.getWorkspace();
if (workspace) {
for (const root of workspace.roots) {
const folderSettingsResource = this.preferencesService.getFolderSettingsResource(root);
if (folderSettingsResource && folderSettingsResource.fsPath === model.uri.fsPath) {
return true;
}
for (const folder of this.workspaceContextService.getWorkspace().folders) {
const folderSettingsResource = this.preferencesService.getFolderSettingsResource(folder.uri);
if (folderSettingsResource && folderSettingsResource.toString() === model.uri.toString()) {
return true;
}
}
@@ -946,3 +1076,54 @@ const focusSettingsFileEditorCommand = new FocusSettingsFileEditorCommand({
kbOpts: { primary: KeyCode.DownArrow }
});
KeybindingsRegistry.registerCommandAndKeybindingRule(focusSettingsFileEditorCommand.toCommandAndKeybindingRule(KeybindingsRegistry.WEIGHT.editorContrib()));
class ClearSearchResultsCommand extends SettingsCommand {
public runCommand(accessor: ServicesAccessor, args: any): void {
const preferencesEditor = this.getPreferencesEditor(accessor);
if (preferencesEditor) {
preferencesEditor.clearSearchResults();
}
}
}
const clearSearchResultsCommand = new ClearSearchResultsCommand({
id: SETTINGS_EDITOR_COMMAND_CLEAR_SEARCH_RESULTS,
precondition: CONTEXT_SETTINGS_SEARCH_FOCUS,
kbOpts: { primary: KeyCode.Escape }
});
KeybindingsRegistry.registerCommandAndKeybindingRule(clearSearchResultsCommand.toCommandAndKeybindingRule(KeybindingsRegistry.WEIGHT.editorContrib()));
class FocusNextSearchResultCommand extends SettingsCommand {
public runCommand(accessor: ServicesAccessor, args: any): void {
const preferencesEditor = this.getPreferencesEditor(accessor);
if (preferencesEditor) {
preferencesEditor.focusNextResult();
}
}
}
const focusNextSearchResultCommand = new FocusNextSearchResultCommand({
id: SETTINGS_EDITOR_COMMAND_FOCUS_NEXT_SETTING,
precondition: CONTEXT_SETTINGS_SEARCH_FOCUS,
kbOpts: { primary: KeyCode.Enter }
});
KeybindingsRegistry.registerCommandAndKeybindingRule(focusNextSearchResultCommand.toCommandAndKeybindingRule(KeybindingsRegistry.WEIGHT.editorContrib()));
class FocusPreviousSearchResultCommand extends SettingsCommand {
public runCommand(accessor: ServicesAccessor, args: any): void {
const preferencesEditor = this.getPreferencesEditor(accessor);
if (preferencesEditor) {
preferencesEditor.focusPreviousResult();
}
}
}
const focusPreviousSearchResultCommand = new FocusPreviousSearchResultCommand({
id: SETTINGS_EDITOR_COMMAND_FOCUS_PREVIOUS_SETTING,
precondition: CONTEXT_SETTINGS_SEARCH_FOCUS,
kbOpts: { primary: KeyMod.Shift | KeyCode.Enter }
});
KeybindingsRegistry.registerCommandAndKeybindingRule(focusPreviousSearchResultCommand.toCommandAndKeybindingRule(KeybindingsRegistry.WEIGHT.editorContrib()));

View File

@@ -6,6 +6,7 @@
import { TPromise } from 'vs/base/common/winjs.base';
import * as nls from 'vs/nls';
import { Delayer } from 'vs/base/common/async';
import { tail } from 'vs/base/common/arrays';
import { Disposable, IDisposable, dispose } from 'vs/base/common/lifecycle';
import { IAction } from 'vs/base/common/actions';
import { IJSONSchema } from 'vs/base/common/jsonSchema';
@@ -15,24 +16,24 @@ import * as editorCommon from 'vs/editor/common/editorCommon';
import { Range, IRange } from 'vs/editor/common/core/range';
import { IConfigurationRegistry, Extensions as ConfigurationExtensions, ConfigurationScope, IConfigurationPropertySchema } from 'vs/platform/configuration/common/configurationRegistry';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IPreferencesService, ISettingsGroup, ISetting, IPreferencesEditorModel, IFilterResult, ISettingsEditorModel } from 'vs/workbench/parts/preferences/common/preferences';
import { SettingsEditorModel, DefaultSettingsEditorModel } from 'vs/workbench/parts/preferences/common/preferencesModels';
import { IPreferencesService, ISettingsGroup, ISetting, IPreferencesEditorModel, IFilterResult, ISettingsEditorModel, IScoredResults } from 'vs/workbench/parts/preferences/common/preferences';
import { SettingsEditorModel, DefaultSettingsEditorModel, WorkspaceConfigurationEditorModel } from 'vs/workbench/parts/preferences/common/preferencesModels';
import { ICodeEditor, IEditorMouseEvent, MouseTargetType } from 'vs/editor/browser/editorBrowser';
import { IContextMenuService, ContextSubMenu } from 'vs/platform/contextview/browser/contextView';
import { SettingsGroupTitleWidget, EditPreferenceWidget, SettingsHeaderWidget } from 'vs/workbench/parts/preferences/browser/preferencesWidgets';
import { SettingsGroupTitleWidget, EditPreferenceWidget, SettingsHeaderWidget, DefaultSettingsHeaderWidget, FloatingClickWidget } from 'vs/workbench/parts/preferences/browser/preferencesWidgets';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { RangeHighlightDecorations } from 'vs/workbench/common/editor/rangeDecorations';
import { IConfigurationEditingService, ConfigurationEditingError, ConfigurationEditingErrorCode, ConfigurationTarget } from 'vs/workbench/services/configuration/common/configurationEditing';
import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles';
import { overrideIdentifierFromKey } from 'vs/platform/configuration/common/model';
import { IMarkerService, IMarkerData } from 'vs/platform/markers/common/markers';
import { IWorkspaceConfigurationService } from 'vs/workbench/services/configuration/common/configuration';
import { IMessageService, Severity } from 'vs/platform/message/common/message';
import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService';
import { ICursorPositionChangedEvent } from 'vs/editor/common/controller/cursorEvents';
import { ModelDecorationOptions } from 'vs/editor/common/model/textModelWithDecorations';
import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace';
import { MarkdownString } from 'vs/base/common/htmlContent';
import { overrideIdentifierFromKey, IConfigurationService, ConfigurationTarget } from 'vs/platform/configuration/common/configuration';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
export interface IPreferencesRenderer<T> extends IDisposable {
preferencesModel: IPreferencesEditorModel<T>;
@@ -41,21 +42,22 @@ export interface IPreferencesRenderer<T> extends IDisposable {
onFocusPreference: Event<T>;
onClearFocusPreference: Event<T>;
onUpdatePreference: Event<{ key: string, value: any, source: T }>;
onTriggeredFuzzy?: Event<void>;
render(): void;
updatePreference(key: string, value: any, source: T): void;
filterPreferences(filterResult: IFilterResult): void;
filterPreferences(filterResult: IFilterResult, fuzzySearchAvailable: boolean): void;
focusPreference(setting: T): void;
clearFocus(setting: T): void;
}
export class UserSettingsRenderer extends Disposable implements IPreferencesRenderer<ISetting> {
private settingHighlighter: SettingHighlighter;
private editSettingActionRenderer: EditSettingRenderer;
private highlightMatchesRenderer: HighlightMatchesRenderer;
private modelChangeDelayer: Delayer<void> = new Delayer<void>(200);
private _associatedPreferencesModel: IPreferencesEditorModel<ISetting>;
private _onFocusPreference: Emitter<ISetting> = new Emitter<ISetting>();
public readonly onFocusPreference: Event<ISetting> = this._onFocusPreference.event;
@@ -68,16 +70,14 @@ export class UserSettingsRenderer extends Disposable implements IPreferencesRend
private filterResult: IFilterResult;
constructor(protected editor: ICodeEditor, public readonly preferencesModel: SettingsEditorModel, private _associatedPreferencesModel: IPreferencesEditorModel<ISetting>,
constructor(protected editor: ICodeEditor, public readonly preferencesModel: SettingsEditorModel,
@IPreferencesService protected preferencesService: IPreferencesService,
@ITelemetryService private telemetryService: ITelemetryService,
@ITextFileService private textFileService: ITextFileService,
@IConfigurationEditingService private configurationEditingService: IConfigurationEditingService,
@IMessageService private messageService: IMessageService,
@IConfigurationService private configurationService: IConfigurationService,
@IInstantiationService protected instantiationService: IInstantiationService
) {
super();
this._register(preferencesModel);
this.settingHighlighter = this._register(instantiationService.createInstance(SettingHighlighter, editor, this._onFocusPreference, this._onClearFocusPreference));
this.highlightMatchesRenderer = this._register(instantiationService.createInstance(HighlightMatchesRenderer, editor));
this.editSettingActionRenderer = this._register(this.instantiationService.createInstance(EditSettingRenderer, this.editor, this.preferencesModel, this.settingHighlighter));
@@ -108,22 +108,16 @@ export class UserSettingsRenderer extends Disposable implements IPreferencesRend
}
public updatePreference(key: string, value: any, source: ISetting): void {
/* __GDPR__
"defaultSettingsActions.copySetting" : {
"userConfigurationKeys" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
}
*/
this.telemetryService.publicLog('defaultSettingsActions.copySetting', { userConfigurationKeys: [key] });
const overrideIdentifier = source.overrideOf ? overrideIdentifierFromKey(source.overrideOf.key) : null;
const resource = this.preferencesModel.uri;
this.configurationEditingService.writeConfiguration(this.preferencesModel.configurationTarget, { key, value }, { donotSave: this.textFileService.isDirty(resource), donotNotifyError: true, scopes: { overrideIdentifier, resource } })
.then(() => this.onSettingUpdated(source), error => {
this.messageService.show(Severity.Error, this.toErrorMessage(error, this.preferencesModel.configurationTarget));
});
}
private toErrorMessage(error: ConfigurationEditingError, target: ConfigurationTarget): string {
switch (error.code) {
case ConfigurationEditingErrorCode.ERROR_INVALID_CONFIGURATION: {
return nls.localize('errorInvalidConfiguration', "Unable to write into settings. Correct errors/warnings in the file and try again.");
};
}
return error.message;
this.configurationService.updateValue(key, value, { overrideIdentifier, resource }, this.preferencesModel.configurationTarget)
.then(() => this.onSettingUpdated(source));
}
private onModelChanged(): void {
@@ -180,19 +174,18 @@ export class UserSettingsRenderer extends Disposable implements IPreferencesRend
export class WorkspaceSettingsRenderer extends UserSettingsRenderer implements IPreferencesRenderer<ISetting> {
private untrustedSettingRenderer: UnsupportedWorkspaceSettingsRenderer;
private unsupportedSettingsRenderer: UnsupportedSettingsRenderer;
private workspaceConfigurationRenderer: WorkspaceConfigurationRenderer;
constructor(editor: ICodeEditor, preferencesModel: SettingsEditorModel, associatedPreferencesModel: IPreferencesEditorModel<ISetting>,
constructor(editor: ICodeEditor, preferencesModel: SettingsEditorModel,
@IPreferencesService preferencesService: IPreferencesService,
@ITelemetryService telemetryService: ITelemetryService,
@ITextFileService textFileService: ITextFileService,
@IConfigurationEditingService configurationEditingService: IConfigurationEditingService,
@IMessageService messageService: IMessageService,
@IConfigurationService configurationService: IConfigurationService,
@IInstantiationService instantiationService: IInstantiationService
) {
super(editor, preferencesModel, associatedPreferencesModel, preferencesService, telemetryService, textFileService, configurationEditingService, messageService, instantiationService);
this.untrustedSettingRenderer = this._register(instantiationService.createInstance(UnsupportedWorkspaceSettingsRenderer, editor, preferencesModel));
super(editor, preferencesModel, preferencesService, telemetryService, textFileService, configurationService, instantiationService);
this.unsupportedSettingsRenderer = this._register(instantiationService.createInstance(UnsupportedSettingsRenderer, editor, preferencesModel));
this.workspaceConfigurationRenderer = this._register(instantiationService.createInstance(WorkspaceConfigurationRenderer, editor, preferencesModel));
}
@@ -202,25 +195,24 @@ export class WorkspaceSettingsRenderer extends UserSettingsRenderer implements I
public render(): void {
super.render();
this.untrustedSettingRenderer.render();
this.unsupportedSettingsRenderer.render();
this.workspaceConfigurationRenderer.render();
}
}
export class FolderSettingsRenderer extends UserSettingsRenderer implements IPreferencesRenderer<ISetting> {
private unsupportedWorkbenchSettingsRenderer: UnsupportedWorkbenchSettingsRenderer;
private unsupportedSettingsRenderer: UnsupportedSettingsRenderer;
constructor(editor: ICodeEditor, preferencesModel: SettingsEditorModel, associatedPreferencesModel: IPreferencesEditorModel<ISetting>,
constructor(editor: ICodeEditor, preferencesModel: SettingsEditorModel,
@IPreferencesService preferencesService: IPreferencesService,
@ITelemetryService telemetryService: ITelemetryService,
@ITextFileService textFileService: ITextFileService,
@IConfigurationEditingService configurationEditingService: IConfigurationEditingService,
@IMessageService messageService: IMessageService,
@IConfigurationService configurationService: IConfigurationService,
@IInstantiationService instantiationService: IInstantiationService
) {
super(editor, preferencesModel, associatedPreferencesModel, preferencesService, telemetryService, textFileService, configurationEditingService, messageService, instantiationService);
this.unsupportedWorkbenchSettingsRenderer = this._register(instantiationService.createInstance(UnsupportedWorkbenchSettingsRenderer, editor, preferencesModel));
super(editor, preferencesModel, preferencesService, telemetryService, textFileService, configurationService, instantiationService);
this.unsupportedSettingsRenderer = this._register(instantiationService.createInstance(UnsupportedSettingsRenderer, editor, preferencesModel));
}
protected createHeader(): void {
@@ -229,7 +221,7 @@ export class FolderSettingsRenderer extends UserSettingsRenderer implements IPre
public render(): void {
super.render();
this.unsupportedWorkbenchSettingsRenderer.render();
this.unsupportedSettingsRenderer.render();
}
}
@@ -242,6 +234,7 @@ export class DefaultSettingsRenderer extends Disposable implements IPreferencesR
private filteredMatchesRenderer: FilteredMatchesRenderer;
private hiddenAreasRenderer: HiddenAreasRenderer;
private editSettingActionRenderer: EditSettingRenderer;
private feedbackWidgetRenderer: FeedbackWidgetRenderer;
private _onUpdatePreference: Emitter<{ key: string, value: any, source: ISetting }> = new Emitter<{ key: string, value: any, source: ISetting }>();
public readonly onUpdatePreference: Event<{ key: string, value: any, source: ISetting }> = this._onUpdatePreference.event;
@@ -252,6 +245,8 @@ export class DefaultSettingsRenderer extends Disposable implements IPreferencesR
private _onClearFocusPreference: Emitter<ISetting> = new Emitter<ISetting>();
public readonly onClearFocusPreference: Event<ISetting> = this._onClearFocusPreference.event;
public readonly onTriggeredFuzzy: Event<void>;
private filterResult: IFilterResult;
constructor(protected editor: ICodeEditor, public readonly preferencesModel: DefaultSettingsEditorModel,
@@ -265,11 +260,17 @@ export class DefaultSettingsRenderer extends Disposable implements IPreferencesR
this.settingsGroupTitleRenderer = this._register(instantiationService.createInstance(SettingsGroupTitleRenderer, editor));
this.filteredMatchesRenderer = this._register(instantiationService.createInstance(FilteredMatchesRenderer, editor));
this.editSettingActionRenderer = this._register(instantiationService.createInstance(EditSettingRenderer, editor, preferencesModel, this.settingHighlighter));
this.feedbackWidgetRenderer = this._register(instantiationService.createInstance(FeedbackWidgetRenderer, editor));
this._register(this.editSettingActionRenderer.onUpdateSetting(e => this._onUpdatePreference.fire(e)));
const paranthesisHidingRenderer = this._register(instantiationService.createInstance(StaticContentHidingRenderer, editor, preferencesModel.settingsGroups));
this.hiddenAreasRenderer = this._register(instantiationService.createInstance(HiddenAreasRenderer, editor, [this.settingsGroupTitleRenderer, this.filteredMatchesRenderer, paranthesisHidingRenderer]));
const parenthesisHidingRenderer = this._register(instantiationService.createInstance(StaticContentHidingRenderer, editor, preferencesModel.settingsGroups));
const hiddenAreasProviders = [this.settingsGroupTitleRenderer, this.filteredMatchesRenderer, parenthesisHidingRenderer];
this.hiddenAreasRenderer = this._register(instantiationService.createInstance(HiddenAreasRenderer, editor, hiddenAreasProviders));
this._register(this.settingsGroupTitleRenderer.onHiddenAreasChanged(() => this.hiddenAreasRenderer.render()));
this.onTriggeredFuzzy = this.settingsHeaderRenderer.onClick;
}
public get associatedPreferencesModel(): IPreferencesEditorModel<ISetting> {
@@ -284,28 +285,32 @@ export class DefaultSettingsRenderer extends Disposable implements IPreferencesR
public render() {
this.settingsGroupTitleRenderer.render(this.preferencesModel.settingsGroups);
this.editSettingActionRenderer.render(this.preferencesModel.settingsGroups, this._associatedPreferencesModel);
this.feedbackWidgetRenderer.render(null);
this.hiddenAreasRenderer.render();
this.settingHighlighter.clear(true);
this.settingsGroupTitleRenderer.showGroup(1);
this.settingsGroupTitleRenderer.showGroup(0);
this.hiddenAreasRenderer.render();
}
public filterPreferences(filterResult: IFilterResult): void {
public filterPreferences(filterResult: IFilterResult, fuzzySearchAvailable: boolean): void {
this.filterResult = filterResult;
if (!filterResult) {
this.settingHighlighter.clear(true);
this.filteredMatchesRenderer.render(null);
this.settingsHeaderRenderer.render(this.preferencesModel.settingsGroups);
this.settingsGroupTitleRenderer.render(this.preferencesModel.settingsGroups);
this.settingsGroupTitleRenderer.showGroup(1);
this.editSettingActionRenderer.render(this.preferencesModel.settingsGroups, this._associatedPreferencesModel);
} else {
this.filteredMatchesRenderer.render(filterResult);
this.settingsHeaderRenderer.render(filterResult.filteredGroups);
if (filterResult) {
this.filteredMatchesRenderer.render(filterResult, this.preferencesModel.settingsGroups);
this.settingsGroupTitleRenderer.render(filterResult.filteredGroups);
this.feedbackWidgetRenderer.render(filterResult);
this.settingsHeaderRenderer.render(filterResult, fuzzySearchAvailable);
this.settingHighlighter.clear(true);
this.editSettingActionRenderer.render(filterResult.filteredGroups, this._associatedPreferencesModel);
} else {
this.settingHighlighter.clear(true);
this.filteredMatchesRenderer.render(null, this.preferencesModel.settingsGroups);
this.feedbackWidgetRenderer.render(null);
this.settingsHeaderRenderer.render(null);
this.settingsGroupTitleRenderer.render(this.preferencesModel.settingsGroups);
this.settingsGroupTitleRenderer.showGroup(0);
this.editSettingActionRenderer.render(this.preferencesModel.settingsGroups, this._associatedPreferencesModel);
}
this.hiddenAreasRenderer.render();
}
@@ -372,6 +377,9 @@ export class StaticContentHidingRenderer extends Disposable implements HiddenAre
get hiddenAreas(): IRange[] {
const model = this.editor.getModel();
// Hide extra chars for "search results" and "commonly used" groups
const lastGroup = tail(this.settingsGroups);
return [
{
startLineNumber: 1,
@@ -385,6 +393,12 @@ export class StaticContentHidingRenderer extends Disposable implements HiddenAre
endLineNumber: this.settingsGroups[0].range.endLineNumber + 4,
endColumn: model.getLineMaxColumn(this.settingsGroups[0].range.endLineNumber + 4)
},
{
startLineNumber: lastGroup.range.endLineNumber + 1,
startColumn: model.getLineMinColumn(lastGroup.range.endLineNumber + 1),
endLineNumber: Math.min(model.getLineCount(), lastGroup.range.endLineNumber + 4),
endColumn: model.getLineMaxColumn(Math.min(model.getLineCount(), lastGroup.range.endLineNumber + 4))
},
{
startLineNumber: model.getLineCount() - 1,
startColumn: model.getLineMinColumn(model.getLineCount() - 1),
@@ -398,20 +412,20 @@ export class StaticContentHidingRenderer extends Disposable implements HiddenAre
class DefaultSettingsHeaderRenderer extends Disposable {
private settingsHeaderWidget: SettingsHeaderWidget;
private settingsHeaderWidget: DefaultSettingsHeaderWidget;
public onClick: Event<void>;
constructor(private editor: ICodeEditor, scope: ConfigurationScope) {
super();
const title = scope === ConfigurationScope.RESOURCE ? nls.localize('defaultFolderSettingsTitle', "Default Folder Settings") : nls.localize('defaultSettingsTitle', "Default Settings");
this.settingsHeaderWidget = this._register(new SettingsHeaderWidget(editor, title));
this.settingsHeaderWidget = this._register(new DefaultSettingsHeaderWidget(editor, title));
this.onClick = this.settingsHeaderWidget.onClick;
}
public render(settingsGroups: ISettingsGroup[]) {
if (settingsGroups.length) {
this.settingsHeaderWidget.setMessage('');
} else {
this.settingsHeaderWidget.setMessage(nls.localize('noSettingsFound', "No Settings Found."));
}
public render(filterResult: IFilterResult, fuzzySearchAvailable = false) {
const hasSettings = !filterResult || filterResult.filteredGroups.length > 0;
const promptFuzzy = fuzzySearchAvailable && filterResult && !filterResult.metadata;
this.settingsHeaderWidget.toggleMessage(hasSettings, promptFuzzy);
}
}
@@ -441,9 +455,17 @@ export class SettingsGroupTitleRenderer extends Disposable implements HiddenArea
public render(settingsGroups: ISettingsGroup[]) {
this.disposeWidgets();
if (!settingsGroups) {
return;
}
this.settingsGroups = settingsGroups.slice();
this.settingsGroupTitleWidgets = [];
for (const group of this.settingsGroups.slice().reverse()) {
if (group.sections.every(sect => sect.settings.length === 0)) {
continue;
}
const settingsGroupTitleWidget = this.instantiationService.createInstance(SettingsGroupTitleWidget, this.editor, group);
settingsGroupTitleWidget.render();
this.settingsGroupTitleWidgets.push(settingsGroupTitleWidget);
@@ -453,9 +475,11 @@ export class SettingsGroupTitleRenderer extends Disposable implements HiddenArea
this.settingsGroupTitleWidgets.reverse();
}
public showGroup(group: number) {
this.hiddenGroups = this.settingsGroups.filter((g, i) => i !== group - 1);
for (const groupTitleWidget of this.settingsGroupTitleWidgets.filter((g, i) => i !== group - 1)) {
public showGroup(groupIdx: number) {
const shownGroup = this.settingsGroupTitleWidgets[groupIdx].settingsGroup;
this.hiddenGroups = this.settingsGroups.filter(g => g !== shownGroup);
for (const groupTitleWidget of this.settingsGroupTitleWidgets.filter(widget => widget.settingsGroup !== shownGroup)) {
groupTitleWidget.toggleCollapse(true);
}
this._onHiddenAreasChanged.fire();
@@ -526,6 +550,120 @@ export class HiddenAreasRenderer extends Disposable {
}
}
export class FeedbackWidgetRenderer extends Disposable {
private static COMMENT_TEXT = 'Modify the below results to match your expectations. Assign scores to indicate their relevance. Replace this comment with any text feedback.';
private _feedbackWidget: FloatingClickWidget;
private _currentResult: IFilterResult;
constructor(private editor: ICodeEditor,
@IInstantiationService private instantiationService: IInstantiationService,
@IWorkbenchEditorService private editorService: IWorkbenchEditorService,
@ITelemetryService private telemetryService: ITelemetryService,
@IMessageService private messageService: IMessageService
) {
super();
}
public render(result: IFilterResult): void {
this._currentResult = result;
if (result && result.metadata) {
this.showWidget();
} else if (this._feedbackWidget) {
this.disposeWidget();
}
}
private showWidget(): void {
if (!this._feedbackWidget) {
this._feedbackWidget = this._register(this.instantiationService.createInstance(FloatingClickWidget, this.editor, 'Provide feedback', null));
this._register(this._feedbackWidget.onClick(() => this.getFeedback()));
this._feedbackWidget.render();
}
}
private getFeedback(): void {
if (!this.telemetryService.isOptedIn) {
this.messageService.show(Severity.Error, 'Can\'t send feedback, user is opted out of telemetry');
return;
}
const result = this._currentResult;
const actualResultNames = Object.keys(result.metadata.scoredResults);
const feedbackQuery = {};
feedbackQuery['comment'] = FeedbackWidgetRenderer.COMMENT_TEXT;
feedbackQuery['queryString'] = result.query;
feedbackQuery['resultScores'] = {};
actualResultNames.forEach(settingKey => {
feedbackQuery['resultScores'][settingKey] = 10;
});
const contents = JSON.stringify(feedbackQuery, undefined, ' ');
this.editorService.openEditor({ contents, language: 'json' }, /*sideBySide=*/true).then(feedbackEditor => {
const sendFeedbackWidget = this._register(this.instantiationService.createInstance(FloatingClickWidget, feedbackEditor.getControl(), 'Send feedback', null));
sendFeedbackWidget.render();
this._register(sendFeedbackWidget.onClick(() => {
this.sendFeedback(feedbackEditor.getControl() as ICodeEditor, result, result.metadata.scoredResults).then(() => {
sendFeedbackWidget.dispose();
this.messageService.show(Severity.Info, 'Feedback sent successfully');
}, err => {
this.messageService.show(Severity.Error, 'Error sending feedback: ' + err.message);
});
}));
});
}
private sendFeedback(feedbackEditor: ICodeEditor, result: IFilterResult, actualResults: IScoredResults): TPromise<void> {
const model = feedbackEditor.getModel();
const expectedQueryLines = model.getLinesContent();
let expectedQuery: any;
try {
expectedQuery = JSON.parse(expectedQueryLines.join('\n'));
} catch (e) {
// invalid JSON
return TPromise.wrapError(new Error('Invalid JSON: ' + e.message));
}
const userComment = expectedQuery.comment === FeedbackWidgetRenderer.COMMENT_TEXT ? undefined : expectedQuery.comment;
/* __GDPR__
"settingsSearchResultFeedback" : {
"query" : { "classification": "CustomContent", "purpose": "FeatureInsight" },
"userComment" : { "classification": "CustomerContent", "purpose": "FeatureInsight" },
"actualResults" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
"expectedResults" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
"url" : { "classification": "CustomerContent", "purpose": "FeatureInsight" },
"duration" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
"timestamp" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
}
*/
return this.telemetryService.publicLog('settingsSearchResultFeedback', {
query: result.query,
userComment,
actualResults,
expectedResults: expectedQuery.resultScores,
url: result.metadata.remoteUrl,
duration: result.metadata.duration,
timestamp: result.metadata.timestamp
});
}
private disposeWidget(): void {
if (this._feedbackWidget) {
this._feedbackWidget.dispose();
this._feedbackWidget = null;
}
}
public dispose() {
this.disposeWidget();
super.dispose();
}
}
export class FilteredMatchesRenderer extends Disposable implements HiddenAreasProvider {
private decorationIds: string[] = [];
@@ -537,7 +675,7 @@ export class FilteredMatchesRenderer extends Disposable implements HiddenAreasPr
super();
}
public render(result: IFilterResult): void {
public render(result: IFilterResult, allSettingsGroups: ISettingsGroup[]): void {
const model = this.editor.getModel();
this.hiddenAreas = [];
this.editor.changeDecorations(changeAccessor => {
@@ -548,6 +686,8 @@ export class FilteredMatchesRenderer extends Disposable implements HiddenAreasPr
this.editor.changeDecorations(changeAccessor => {
this.decorationIds = changeAccessor.deltaDecorations(this.decorationIds, result.matches.map(match => this.createDecoration(match, model)));
});
} else {
this.hiddenAreas = this.computeHiddenRanges(allSettingsGroups, allSettingsGroups, model);
}
}
@@ -558,6 +698,7 @@ export class FilteredMatchesRenderer extends Disposable implements HiddenAreasPr
stickiness: editorCommon.TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges,
className: 'findMatch'
}
};
}
@@ -565,7 +706,7 @@ export class FilteredMatchesRenderer extends Disposable implements HiddenAreasPr
const notMatchesRanges: IRange[] = [];
for (const group of allSettingsGroups) {
const filteredGroup = filteredGroups.filter(g => g.title === group.title)[0];
if (!filteredGroup) {
if (!filteredGroup || filteredGroup.sections.every(sect => sect.settings.length === 0)) {
notMatchesRanges.push({
startLineNumber: group.range.startLineNumber - 1,
startColumn: model.getLineMinColumn(group.range.startLineNumber - 1),
@@ -796,10 +937,14 @@ class EditSettingRenderer extends Disposable {
let configurationNode = configurationMap[setting.key];
if (configurationNode) {
if (this.isDefaultSettings()) {
if (setting.key === 'launch') {
// Do not show because of https://github.com/Microsoft/vscode/issues/32593
return false;
}
return true;
}
if (configurationNode.type === 'boolean' || configurationNode.enum) {
if ((<SettingsEditorModel>this.masterSettingsModel).configurationTarget !== ConfigurationTarget.FOLDER) {
if ((<SettingsEditorModel>this.masterSettingsModel).configurationTarget !== ConfigurationTarget.WORKSPACE_FOLDER) {
return true;
}
if (configurationNode.scope === ConfigurationScope.RESOURCE) {
@@ -847,7 +992,7 @@ class EditSettingRenderer extends Disposable {
}
private onEditSettingClicked(editPreferenceWidget: EditPreferenceWidget<ISetting>, e: IEditorMouseEvent): void {
const anchor = { x: e.event.posx + 1, y: e.event.posy + 10 };
const anchor = { x: e.event.posx, y: e.event.posy + 10 };
const actions = this.getSettings(editPreferenceWidget.getLine()).length === 1 ? this.getActions(editPreferenceWidget.preferences[0], this.getConfigurationsMap()[editPreferenceWidget.preferences[0].key])
: editPreferenceWidget.preferences.map(setting => new ContextSubMenu(setting.key, this.getActions(setting, this.getConfigurationsMap()[setting.key])));
this.contextMenuService.showContextMenu({
@@ -888,8 +1033,8 @@ class EditSettingRenderer extends Disposable {
}
private getDefaultActions(setting: ISetting): IAction[] {
const settingInOtherModel = this.associatedPreferencesModel.getPreference(setting.key);
if (this.isDefaultSettings()) {
const settingInOtherModel = this.associatedPreferencesModel.getPreference(setting.key);
return [<IAction>{
id: 'setDefaultValue',
label: settingInOtherModel ? nls.localize('replaceDefaultValue', "Replace in Settings") : nls.localize('copyDefaultValue', "Copy to Settings"),
@@ -945,17 +1090,72 @@ class SettingHighlighter extends Disposable {
}
}
class UnsupportedWorkspaceSettingsRenderer extends Disposable {
class UnsupportedSettingsRenderer extends Disposable {
constructor(private editor: editorCommon.ICommonCodeEditor, private workspaceSettingsEditorModel: SettingsEditorModel,
private decorationIds: string[] = [];
private renderingDelayer: Delayer<void> = new Delayer<void>(200);
constructor(
private editor: editorCommon.ICommonCodeEditor,
private settingsEditorModel: SettingsEditorModel,
@IWorkspaceConfigurationService private configurationService: IWorkspaceConfigurationService,
@IMarkerService private markerService: IMarkerService
@IMarkerService private markerService: IMarkerService,
@IEnvironmentService private environmentService: IEnvironmentService
) {
super();
this._register(this.configurationService.onDidUpdateConfiguration(() => this.render()));
this._register(this.editor.getModel().onDidChangeContent(() => this.renderingDelayer.trigger(() => this.render())));
}
private getMarkerMessage(settingKey): string {
public render(): void {
const configurationRegistry = Registry.as<IConfigurationRegistry>(ConfigurationExtensions.Configuration).getConfigurationProperties();
const ranges: IRange[] = [];
const markerData: IMarkerData[] = [];
for (const settingsGroup of this.settingsEditorModel.settingsGroups) {
for (const section of settingsGroup.sections) {
for (const setting of section.settings) {
if (this.settingsEditorModel.configurationTarget === ConfigurationTarget.WORKSPACE || this.settingsEditorModel.configurationTarget === ConfigurationTarget.WORKSPACE_FOLDER) {
// Show warnings for executable settings
if (configurationRegistry[setting.key] && configurationRegistry[setting.key].isExecutable) {
markerData.push({
severity: Severity.Warning,
startLineNumber: setting.keyRange.startLineNumber,
startColumn: setting.keyRange.startColumn,
endLineNumber: setting.keyRange.endLineNumber,
endColumn: setting.keyRange.endColumn,
message: this.getMarkerMessage(setting.key)
});
}
}
if (this.settingsEditorModel.configurationTarget === ConfigurationTarget.WORKSPACE_FOLDER) {
// Dim and show information for window settings
if (configurationRegistry[setting.key] && configurationRegistry[setting.key].scope === ConfigurationScope.WINDOW) {
ranges.push({
startLineNumber: setting.keyRange.startLineNumber,
startColumn: setting.keyRange.startColumn - 1,
endLineNumber: setting.valueRange.endLineNumber,
endColumn: setting.valueRange.endColumn
});
}
}
}
}
}
if (markerData.length) {
this.markerService.changeOne('preferencesEditor', this.settingsEditorModel.uri, markerData);
} else {
this.markerService.remove('preferencesEditor', [this.settingsEditorModel.uri]);
}
this.editor.changeDecorations(changeAccessor => this.decorationIds = changeAccessor.deltaDecorations(this.decorationIds, ranges.map(range => this.createDecoration(range, this.editor.getModel()))));
}
private createDecoration(range: IRange, model: editorCommon.IModel): editorCommon.IModelDeltaDecoration {
return {
range,
options: !this.environmentService.isBuilt || this.environmentService.isExtensionDevelopment ? UnsupportedSettingsRenderer._DIM_CONFIGUARATION_DEV_MODE : UnsupportedSettingsRenderer._DIM_CONFIGUARATION_
};
}
private getMarkerMessage(settingKey: string): string {
switch (settingKey) {
case 'php.validate.executablePath':
return nls.localize('unsupportedPHPExecutablePathSetting', "This setting must be a User Setting. To configure PHP for the workspace, open a PHP file and click on 'PHP Path' in the status bar.");
@@ -964,67 +1164,14 @@ class UnsupportedWorkspaceSettingsRenderer extends Disposable {
}
}
public render(): void {
const unsupportedWorkspaceKeys = this.configurationService.getUnsupportedWorkspaceKeys();
if (unsupportedWorkspaceKeys.length) {
const markerData: IMarkerData[] = [];
for (const unsupportedKey of unsupportedWorkspaceKeys) {
const setting = this.workspaceSettingsEditorModel.getPreference(unsupportedKey);
if (setting) {
markerData.push({
severity: Severity.Warning,
startLineNumber: setting.keyRange.startLineNumber,
startColumn: setting.keyRange.startColumn,
endLineNumber: setting.keyRange.endLineNumber,
endColumn: setting.keyRange.endColumn,
message: this.getMarkerMessage(unsupportedKey)
});
}
}
if (markerData.length) {
this.markerService.changeOne('preferencesEditor', this.workspaceSettingsEditorModel.uri, markerData);
} else {
this.markerService.remove('preferencesEditor', [this.workspaceSettingsEditorModel.uri]);
}
}
}
public dispose(): void {
this.markerService.remove('preferencesEditor', [this.workspaceSettingsEditorModel.uri]);
super.dispose();
}
}
class UnsupportedWorkbenchSettingsRenderer extends Disposable {
private decorationIds: string[] = [];
private renderingDelayer: Delayer<void> = new Delayer<void>(200);
constructor(private editor: editorCommon.ICommonCodeEditor, private workspaceSettingsEditorModel: SettingsEditorModel,
@IWorkspaceConfigurationService private configurationService: IWorkspaceConfigurationService,
) {
super();
this._register(this.editor.getModel().onDidChangeContent(() => this.renderingDelayer.trigger(() => this.render())));
}
public render(): void {
const ranges: IRange[] = [];
const configurationRegistry = Registry.as<IConfigurationRegistry>(ConfigurationExtensions.Configuration).getConfigurationProperties();
for (const settingsGroup of this.workspaceSettingsEditorModel.settingsGroups) {
for (const section of settingsGroup.sections) {
for (const setting of section.settings) {
if (configurationRegistry[setting.key] && configurationRegistry[setting.key].scope === ConfigurationScope.WINDOW) {
ranges.push({
startLineNumber: setting.keyRange.startLineNumber,
startColumn: setting.keyRange.startColumn - 1,
endLineNumber: setting.valueRange.endLineNumber,
endColumn: setting.valueRange.endColumn
});
}
}
}
this.markerService.remove('preferencesEditor', [this.settingsEditorModel.uri]);
if (this.decorationIds) {
this.decorationIds = this.editor.changeDecorations(changeAccessor => {
return changeAccessor.deltaDecorations(this.decorationIds, []);
});
}
this.editor.changeDecorations(changeAccessor => this.decorationIds = changeAccessor.deltaDecorations(this.decorationIds, ranges.map(range => this.createDecoration(range, this.editor.getModel()))));
super.dispose();
}
private static _DIM_CONFIGUARATION_ = ModelDecorationOptions.register({
@@ -1034,21 +1181,12 @@ class UnsupportedWorkbenchSettingsRenderer extends Disposable {
hoverMessage: new MarkdownString().appendText(nls.localize('unsupportedWorkbenchSetting', "This setting cannot be applied now. It will be applied when you open this folder directly."))
});
private createDecoration(range: IRange, model: editorCommon.IModel): editorCommon.IModelDeltaDecoration {
return {
range,
options: UnsupportedWorkbenchSettingsRenderer._DIM_CONFIGUARATION_
};
}
public dispose(): void {
if (this.decorationIds) {
this.decorationIds = this.editor.changeDecorations(changeAccessor => {
return changeAccessor.deltaDecorations(this.decorationIds, []);
});
}
super.dispose();
}
private static _DIM_CONFIGUARATION_DEV_MODE = ModelDecorationOptions.register({
stickiness: editorCommon.TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges,
inlineClassName: 'dim-configuration',
beforeContentClassName: 'unsupportedWorkbenhSettingInfo',
hoverMessage: new MarkdownString().appendText(nls.localize('unsupportedWorkbenchSettingDevMode', "This setting cannot be applied now. It will be applied if you define it's scope as 'resource' while registering, or when you open this folder directly."))
});
}
class WorkspaceConfigurationRenderer extends Disposable {
@@ -1064,11 +1202,11 @@ class WorkspaceConfigurationRenderer extends Disposable {
}
public render(): void {
if (this.workspaceContextService.hasMultiFolderWorkspace()) {
if (this.workspaceContextService.getWorkbenchState() === WorkbenchState.WORKSPACE && this.workspaceSettingsEditorModel instanceof WorkspaceConfigurationEditorModel) {
this.editor.changeDecorations(changeAccessor => this.decorationIds = changeAccessor.deltaDecorations(this.decorationIds, []));
const ranges: IRange[] = [];
for (const settingsGroup of this.workspaceSettingsEditorModel.settingsGroups) {
for (const settingsGroup of this.workspaceSettingsEditorModel.configurationGroups) {
for (const section of settingsGroup.sections) {
for (const setting of section.settings) {
if (setting.key !== 'settings') {

View File

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

View File

@@ -8,18 +8,16 @@ import * as network from 'vs/base/common/network';
import { TPromise } from 'vs/base/common/winjs.base';
import * as nls from 'vs/nls';
import URI from 'vs/base/common/uri';
import * as paths from 'vs/base/common/paths';
import { ResourceMap } from 'vs/base/common/map';
import * as labels from 'vs/base/common/labels';
import * as strings from 'vs/base/common/strings';
import { Disposable } from 'vs/base/common/lifecycle';
import { Emitter } from 'vs/base/common/event';
import { EditorInput } from 'vs/workbench/common/editor';
import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService';
import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace';
import { IWorkspaceConfigurationService } from 'vs/workbench/services/configuration/common/configuration';
import { Position as EditorPosition, IEditor } from 'vs/platform/editor/common/editor';
import { ICommonCodeEditor } from 'vs/editor/common/editorCommon';
import { Position as EditorPosition, IEditor, IEditorOptions } from 'vs/platform/editor/common/editor';
import { ICommonCodeEditor, IModel } from 'vs/editor/common/editorCommon';
import { IEditorGroupService } from 'vs/workbench/services/group/common/groupService';
import { IStorageService } from 'vs/platform/storage/common/storage';
import { IFileService, FileOperationError, FileOperationResult } from 'vs/platform/files/common/files';
@@ -27,9 +25,8 @@ import { IMessageService, Severity, IChoiceService } from 'vs/platform/message/c
import { IExtensionService } from 'vs/platform/extensions/common/extensions';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { IConfigurationEditingService, ConfigurationTarget } from 'vs/workbench/services/configuration/common/configurationEditing';
import { IPreferencesService, IPreferencesEditorModel, ISetting, getSettingsTargetName } from 'vs/workbench/parts/preferences/common/preferences';
import { SettingsEditorModel, DefaultSettingsEditorModel, DefaultKeybindingsEditorModel, defaultKeybindingsContents, WorkspaceConfigModel } from 'vs/workbench/parts/preferences/common/preferencesModels';
import { IPreferencesService, IPreferencesEditorModel, ISetting, getSettingsTargetName, FOLDER_SETTINGS_PATH, DEFAULT_SETTINGS_EDITOR_SETTING } from 'vs/workbench/parts/preferences/common/preferences';
import { SettingsEditorModel, DefaultSettingsEditorModel, DefaultKeybindingsEditorModel, defaultKeybindingsContents, DefaultSettingsModel, WorkspaceConfigurationEditorModel } from 'vs/workbench/parts/preferences/common/preferencesModels';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { DefaultPreferencesEditorInput, PreferencesEditorInput } from 'vs/workbench/parts/preferences/browser/preferencesEditor';
import { KeybindingsEditorInput } from 'vs/workbench/parts/preferences/browser/keybindingsEditor';
@@ -41,15 +38,9 @@ import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
import { IModelService } from 'vs/editor/common/services/modelService';
import { IJSONEditingService } from 'vs/workbench/services/configuration/common/jsonEditing';
import { ConfigurationScope } from 'vs/platform/configuration/common/configurationRegistry';
interface IWorkbenchSettingsConfiguration {
workbench: {
settings: {
openDefaultSettings: boolean;
}
};
}
import { ConfigurationTarget } from 'vs/platform/configuration/common/configuration';
import { IModeService } from 'vs/editor/common/services/modeService';
import { parse } from 'vs/base/common/json';
const emptyEditableSettingsContent = '{\n}';
@@ -57,12 +48,15 @@ export class PreferencesService extends Disposable implements IPreferencesServic
_serviceBrand: any;
// TODO:@sandy merge these models into editor inputs by extending resource editor model
private defaultPreferencesEditorModels: ResourceMap<TPromise<IPreferencesEditorModel<any>>>;
private lastOpenedSettingsInput: PreferencesEditorInput = null;
private _onDispose: Emitter<void> = new Emitter<void>();
private _defaultSettingsUriCounter = 0;
private _defaultSettingsContentModel: DefaultSettingsModel;
private _defaultResourceSettingsUriCounter = 0;
private _defaultResourceSettingsContentModel: DefaultSettingsModel;
constructor(
@IWorkbenchEditorService private editorService: IWorkbenchEditorService,
@IEditorGroupService private editorGroupService: IEditorGroupService,
@@ -76,14 +70,13 @@ export class PreferencesService extends Disposable implements IPreferencesServic
@IEnvironmentService private environmentService: IEnvironmentService,
@ITelemetryService private telemetryService: ITelemetryService,
@ITextModelService private textModelResolverService: ITextModelService,
@IConfigurationEditingService private configurationEditingService: IConfigurationEditingService,
@IExtensionService private extensionService: IExtensionService,
@IKeybindingService keybindingService: IKeybindingService,
@IModelService private modelService: IModelService,
@IJSONEditingService private jsonEditingService: IJSONEditingService
@IJSONEditingService private jsonEditingService: IJSONEditingService,
@IModeService private modeService: IModeService
) {
super();
this.defaultPreferencesEditorModels = new ResourceMap<TPromise<IPreferencesEditorModel<any>>>();
this.editorGroupService.onEditorsChanged(() => {
const activeEditorInput = this.editorService.getActiveEditorInput();
if (activeEditorInput instanceof PreferencesEditorInput) {
@@ -103,10 +96,7 @@ export class PreferencesService extends Disposable implements IPreferencesServic
});
}
readonly defaultSettingsResource = URI.from({ scheme: network.Schemas.vscode, authority: 'defaultsettings', path: '/settings.json' });
readonly defaultResourceSettingsResource = URI.from({ scheme: network.Schemas.vscode, authority: 'defaultsettings', path: '/resourceSettings.json' });
readonly defaultKeybindingsResource = URI.from({ scheme: network.Schemas.vscode, authority: 'defaultsettings', path: '/keybindings.json' });
private readonly workspaceConfigSettingsResource = URI.from({ scheme: network.Schemas.vscode, authority: 'settings', path: '/workspaceSettings.json' });
get userSettingsResource(): URI {
return this.getEditableSettingsURI(ConfigurationTarget.USER);
@@ -117,89 +107,66 @@ export class PreferencesService extends Disposable implements IPreferencesServic
}
getFolderSettingsResource(resource: URI): URI {
return this.getEditableSettingsURI(ConfigurationTarget.FOLDER, resource);
return this.getEditableSettingsURI(ConfigurationTarget.WORKSPACE_FOLDER, resource);
}
resolveContent(uri: URI): TPromise<string> {
const workspaceSettingsUri = this.getEditableSettingsURI(ConfigurationTarget.WORKSPACE);
if (workspaceSettingsUri && workspaceSettingsUri.fsPath === uri.fsPath) {
return this.resolveSettingsContentFromWorkspaceConfiguration();
resolveModel(uri: URI): TPromise<IModel> {
if (this.isDefaultSettingsResource(uri) || this.isDefaultResourceSettingsResource(uri)) {
return this.extensionService.onReady()
.then(() => {
const scope = this.isDefaultSettingsResource(uri) ? ConfigurationScope.WINDOW : ConfigurationScope.RESOURCE;
const settingsModel = this.getDefaultSettingsModel(scope);
const mode = this.modeService.getOrCreateMode('json');
const model = this._register(this.modelService.createModel(settingsModel.content, mode, uri));
return model;
});
}
return this.createPreferencesEditorModel(uri)
.then(preferencesEditorModel => preferencesEditorModel ? preferencesEditorModel.content : null);
if (this.defaultKeybindingsResource.toString() === uri.toString()) {
const defaultKeybindingsEditorModel = this.instantiationService.createInstance(DefaultKeybindingsEditorModel, uri);
const mode = this.modeService.getOrCreateMode('json');
const model = this._register(this.modelService.createModel(defaultKeybindingsEditorModel.content, mode, uri));
return TPromise.as(model);
}
return TPromise.as(null);
}
createPreferencesEditorModel(uri: URI): TPromise<IPreferencesEditorModel<any>> {
let promise = this.defaultPreferencesEditorModels.get(uri);
if (promise) {
return promise;
if (this.isDefaultSettingsResource(uri) || this.isDefaultResourceSettingsResource(uri)) {
return this.createDefaultSettingsEditorModel(uri);
}
if (this.defaultSettingsResource.fsPath === uri.fsPath) {
promise = TPromise.join<any>([this.extensionService.onReady(), this.fetchMostCommonlyUsedSettings()])
.then(result => {
const mostCommonSettings = result[1];
const model = this.instantiationService.createInstance(DefaultSettingsEditorModel, uri, mostCommonSettings, ConfigurationScope.WINDOW);
return model;
});
this.defaultPreferencesEditorModels.set(uri, promise);
return promise;
}
if (this.defaultResourceSettingsResource.fsPath === uri.fsPath) {
promise = TPromise.join<any>([this.extensionService.onReady(), this.fetchMostCommonlyUsedSettings()])
.then(result => {
const mostCommonSettings = result[1];
const model = this.instantiationService.createInstance(DefaultSettingsEditorModel, uri, mostCommonSettings, ConfigurationScope.RESOURCE);
return model;
});
this.defaultPreferencesEditorModels.set(uri, promise);
return promise;
}
if (this.defaultKeybindingsResource.fsPath === uri.fsPath) {
const model = this.instantiationService.createInstance(DefaultKeybindingsEditorModel, uri);
promise = TPromise.wrap(model);
this.defaultPreferencesEditorModels.set(uri, promise);
return promise;
}
if (this.workspaceConfigSettingsResource.fsPath === uri.fsPath) {
promise = this.createEditableSettingsEditorModel(ConfigurationTarget.WORKSPACE, uri);
this.defaultPreferencesEditorModels.set(uri, promise);
return promise;
}
if (this.getEditableSettingsURI(ConfigurationTarget.USER).fsPath === uri.fsPath) {
if (this.getEditableSettingsURI(ConfigurationTarget.USER).toString() === uri.toString()) {
return this.createEditableSettingsEditorModel(ConfigurationTarget.USER, uri);
}
const workspaceSettingsUri = this.getEditableSettingsURI(ConfigurationTarget.WORKSPACE);
if (workspaceSettingsUri && workspaceSettingsUri.fsPath === uri.fsPath) {
if (workspaceSettingsUri && workspaceSettingsUri.toString() === uri.toString()) {
return this.createEditableSettingsEditorModel(ConfigurationTarget.WORKSPACE, workspaceSettingsUri);
}
if (this.contextService.hasMultiFolderWorkspace()) {
return this.createEditableSettingsEditorModel(ConfigurationTarget.FOLDER, uri);
if (this.contextService.getWorkbenchState() === WorkbenchState.WORKSPACE) {
return this.createEditableSettingsEditorModel(ConfigurationTarget.WORKSPACE_FOLDER, uri);
}
return TPromise.wrap<IPreferencesEditorModel<any>>(null);
}
openGlobalSettings(): TPromise<IEditor> {
return this.doOpenSettings(ConfigurationTarget.USER, this.userSettingsResource);
openGlobalSettings(options?: IEditorOptions, position?: EditorPosition): TPromise<IEditor> {
return this.doOpenSettings(ConfigurationTarget.USER, this.userSettingsResource, options, position);
}
openWorkspaceSettings(): TPromise<IEditor> {
if (!this.contextService.hasWorkspace()) {
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"));
return TPromise.as(null);
}
return this.doOpenSettings(ConfigurationTarget.WORKSPACE, this.workspaceSettingsResource);
return this.doOpenSettings(ConfigurationTarget.WORKSPACE, this.workspaceSettingsResource, options, position);
}
openFolderSettings(folder: URI): TPromise<IEditor> {
return this.doOpenSettings(ConfigurationTarget.FOLDER, this.getEditableSettingsURI(ConfigurationTarget.FOLDER, folder));
openFolderSettings(folder: URI, options?: IEditorOptions, position?: EditorPosition): TPromise<IEditor> {
return this.doOpenSettings(ConfigurationTarget.WORKSPACE_FOLDER, this.getEditableSettingsURI(ConfigurationTarget.WORKSPACE_FOLDER, folder), options, position);
}
switchSettings(target: ConfigurationTarget, resource: URI): TPromise<void> {
@@ -223,6 +190,11 @@ export class PreferencesService extends Disposable implements IPreferencesServic
}
openGlobalKeybindingSettings(textual: boolean): TPromise<void> {
/* __GDPR__
"openKeybindings" : {
"textual" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
}
*/
this.telemetryService.publicLog('openKeybindings', { textual });
if (textual) {
const emptyContents = '// ' + nls.localize('emptyKeybindingsHeader', "Place your key bindings in this file to overwrite the defaults") + '\n[\n]';
@@ -254,30 +226,45 @@ export class PreferencesService extends Disposable implements IPreferencesServic
});
}
private doOpenSettings(configurationTarget: ConfigurationTarget, resource: URI): TPromise<IEditor> {
const openDefaultSettings = !!this.configurationService.getConfiguration<IWorkbenchSettingsConfiguration>().workbench.settings.openDefaultSettings;
private doOpenSettings(configurationTarget: ConfigurationTarget, resource: URI, options?: IEditorOptions, position?: EditorPosition): TPromise<IEditor> {
const openDefaultSettings = !!this.configurationService.getValue(DEFAULT_SETTINGS_EDITOR_SETTING);
return this.getOrCreateEditableSettingsEditorInput(configurationTarget, resource)
.then(editableSettingsEditorInput => {
if (!options) {
options = { pinned: true };
} else {
options.pinned = true;
}
if (openDefaultSettings) {
const defaultPreferencesEditorInput = this.instantiationService.createInstance(DefaultPreferencesEditorInput, this.getDefaultSettingsResource(configurationTarget));
const preferencesEditorInput = new PreferencesEditorInput(this.getPreferencesEditorInputName(configurationTarget, resource), editableSettingsEditorInput.getDescription(), defaultPreferencesEditorInput, <EditorInput>editableSettingsEditorInput);
this.lastOpenedSettingsInput = preferencesEditorInput;
return this.editorService.openEditor(preferencesEditorInput, { pinned: true });
return this.editorService.openEditor(preferencesEditorInput, options, position);
}
return this.editorService.openEditor(editableSettingsEditorInput, { pinned: true });
return this.editorService.openEditor(editableSettingsEditorInput, options, position);
});
}
private isDefaultSettingsResource(uri: URI): boolean {
return uri.authority === 'defaultsettings' && uri.scheme === network.Schemas.vscode && !!uri.path.match(/\/(\d+\/)?settings\.json$/);
}
private isDefaultResourceSettingsResource(uri: URI): boolean {
return uri.authority === 'defaultsettings' && uri.scheme === network.Schemas.vscode && !!uri.path.match(/\/(\d+\/)?resourceSettings\.json$/);
}
private getDefaultSettingsResource(configurationTarget: ConfigurationTarget): URI {
if (configurationTarget === ConfigurationTarget.FOLDER) {
return this.defaultResourceSettingsResource;
if (configurationTarget === ConfigurationTarget.WORKSPACE_FOLDER) {
return URI.from({ scheme: network.Schemas.vscode, authority: 'defaultsettings', path: `/${this._defaultResourceSettingsUriCounter++}/resourceSettings.json` });
} else {
return URI.from({ scheme: network.Schemas.vscode, authority: 'defaultsettings', path: `/${this._defaultSettingsUriCounter++}/settings.json` });
}
return this.defaultSettingsResource;
}
private getPreferencesEditorInputName(target: ConfigurationTarget, resource: URI): string {
const name = getSettingsTargetName(target, resource, this.contextService);
return target === ConfigurationTarget.FOLDER ? nls.localize('folderSettingsName', "{0} (Folder Settings)", name) : name;
return target === ConfigurationTarget.WORKSPACE_FOLDER ? nls.localize('folderSettingsName', "{0} (Folder Settings)", name) : name;
}
private getOrCreateEditableSettingsEditorInput(target: ConfigurationTarget, resource: URI): TPromise<EditorInput> {
@@ -288,9 +275,10 @@ export class PreferencesService extends Disposable implements IPreferencesServic
private createEditableSettingsEditorModel(configurationTarget: ConfigurationTarget, resource: URI): TPromise<SettingsEditorModel> {
const settingsUri = this.getEditableSettingsURI(configurationTarget, resource);
if (settingsUri) {
if (settingsUri.fsPath === this.workspaceConfigSettingsResource.fsPath) {
return TPromise.join([this.textModelResolverService.createModelReference(settingsUri), this.textModelResolverService.createModelReference(this.contextService.getWorkspace().configuration)])
.then(([reference, workspaceConfigReference]) => this.instantiationService.createInstance(WorkspaceConfigModel, reference, workspaceConfigReference, configurationTarget, this._onDispose.event));
const workspace = this.contextService.getWorkspace();
if (workspace.configuration && workspace.configuration.toString() === settingsUri.toString()) {
return this.textModelResolverService.createModelReference(settingsUri)
.then(reference => this.instantiationService.createInstance(WorkspaceConfigurationEditorModel, reference, configurationTarget));
}
return this.textModelResolverService.createModelReference(settingsUri)
.then(reference => this.instantiationService.createInstance(SettingsEditorModel, reference, configurationTarget));
@@ -298,17 +286,27 @@ export class PreferencesService extends Disposable implements IPreferencesServic
return TPromise.wrap<SettingsEditorModel>(null);
}
private resolveSettingsContentFromWorkspaceConfiguration(): TPromise<string> {
if (this.contextService.hasMultiFolderWorkspace()) {
return this.textModelResolverService.createModelReference(this.contextService.getWorkspace().configuration)
.then(reference => {
const model = reference.object.textEditorModel;
const settingsContent = WorkspaceConfigModel.getSettingsContentFromConfigContent(model.getValue());
reference.dispose();
return TPromise.as(settingsContent ? settingsContent : emptyEditableSettingsContent);
});
private createDefaultSettingsEditorModel(defaultSettingsUri: URI): TPromise<DefaultSettingsEditorModel> {
return this.textModelResolverService.createModelReference(defaultSettingsUri)
.then(reference => {
const scope = this.isDefaultSettingsResource(defaultSettingsUri) ? ConfigurationScope.WINDOW : ConfigurationScope.RESOURCE;
return this.instantiationService.createInstance(DefaultSettingsEditorModel, defaultSettingsUri, reference, scope, this.getDefaultSettingsModel(scope).settingsGroups);
});
}
private getDefaultSettingsModel(scope: ConfigurationScope): DefaultSettingsModel {
switch (scope) {
case ConfigurationScope.WINDOW:
if (!this._defaultSettingsContentModel) {
this._defaultSettingsContentModel = new DefaultSettingsModel(this.getMostCommonlyUsedSettings(), scope);
}
return this._defaultSettingsContentModel;
case ConfigurationScope.RESOURCE:
if (!this._defaultResourceSettingsContentModel) {
this._defaultResourceSettingsContentModel = new DefaultSettingsModel(this.getMostCommonlyUsedSettings(), scope);
}
return this._defaultResourceSettingsContentModel;
}
return TPromise.as(null);
}
private getEditableSettingsURI(configurationTarget: ConfigurationTarget, resource?: URI): URI {
@@ -316,32 +314,27 @@ export class PreferencesService extends Disposable implements IPreferencesServic
case ConfigurationTarget.USER:
return URI.file(this.environmentService.appSettingsPath);
case ConfigurationTarget.WORKSPACE:
if (this.contextService.getWorkbenchState() === WorkbenchState.EMPTY) {
return null;
}
const workspace = this.contextService.getWorkspace();
if (this.contextService.hasFolderWorkspace()) {
// {{SQL CARBON EDIT}}
return this.toResource(paths.join('.sqlops', 'settings.json'), workspace.roots[0]);
}
if (this.contextService.hasMultiFolderWorkspace()) {
return workspace.configuration;
}
return null;
case ConfigurationTarget.FOLDER:
const root = this.contextService.getRoot(resource);
// {{SQL CARBON EDIT}}
return root ? this.toResource(paths.join('.sqlops', 'settings.json'), root) : null;
return workspace.configuration || workspace.folders[0].toResource(FOLDER_SETTINGS_PATH);
case ConfigurationTarget.WORKSPACE_FOLDER:
const folder = this.contextService.getWorkspaceFolder(resource);
return folder ? folder.toResource(FOLDER_SETTINGS_PATH) : null;
}
return null;
}
private toResource(relativePath: string, root: URI): URI {
return URI.file(paths.join(root.fsPath, relativePath));
}
private createSettingsIfNotExists(target: ConfigurationTarget, resource: URI): TPromise<void> {
if (this.contextService.hasMultiFolderWorkspace() && target === ConfigurationTarget.WORKSPACE) {
if (!this.configurationService.keys().workspace.length) {
return this.jsonEditingService.write(resource, { key: 'settings', value: {} }, true).then(null, () => { });
}
if (this.contextService.getWorkbenchState() === WorkbenchState.WORKSPACE && target === ConfigurationTarget.WORKSPACE) {
return this.fileService.resolveContent(this.contextService.getWorkspace().configuration)
.then(content => {
if (Object.keys(parse(content.value)).indexOf('settings') === -1) {
return this.jsonEditingService.write(resource, { key: 'settings', value: {} }, true).then(null, () => { });
}
return null;
});
}
return this.createIfNotExists(resource, emptyEditableSettingsContent).then(() => { });
}
@@ -358,8 +351,8 @@ export class PreferencesService extends Disposable implements IPreferencesServic
});
}
private fetchMostCommonlyUsedSettings(): TPromise<string[]> {
return TPromise.wrap([
private getMostCommonlyUsedSettings(): string[] {
return [
'files.autoSave',
'editor.fontSize',
'editor.fontFamily',
@@ -371,7 +364,7 @@ export class PreferencesService extends Disposable implements IPreferencesServic
'editor.wordWrap',
'files.exclude',
'files.associations'
]);
];
}
private getPosition(language: string, codeEditor: ICommonCodeEditor): TPromise<IPosition> {
@@ -397,7 +390,7 @@ export class PreferencesService extends Disposable implements IPreferencesServic
}
return { lineNumber: setting.valueRange.startLineNumber, column: setting.valueRange.startColumn + 1 };
}
return this.configurationEditingService.writeConfiguration(ConfigurationTarget.USER, { key: languageKey, value: {} }, { donotSave: true })
return this.configurationService.updateValue(languageKey, {}, ConfigurationTarget.USER)
.then(() => {
setting = settingsModel.getPreference(languageKey);
let content = eol + this.spaces(2, configuration) + eol + this.spaces(1, configuration);
@@ -416,7 +409,6 @@ export class PreferencesService extends Disposable implements IPreferencesServic
public dispose(): void {
this._onDispose.fire();
this.defaultPreferencesEditorModels.clear();
super.dispose();
}
}

View File

@@ -10,6 +10,7 @@ import * as DOM from 'vs/base/browser/dom';
import { TPromise } from 'vs/base/common/winjs.base';
import { Disposable } from 'vs/base/common/lifecycle';
import { Widget } from 'vs/base/browser/ui/widget';
import { Checkbox } from 'vs/base/browser/ui/checkbox/checkbox';
import Event, { Emitter } from 'vs/base/common/event';
import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent';
import { KeyCode } from 'vs/base/common/keyCodes';
@@ -20,9 +21,9 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti
import { IContextViewService, IContextMenuService } from 'vs/platform/contextview/browser/contextView';
import { ISettingsGroup, IPreferencesService, getSettingsTargetName } from 'vs/workbench/parts/preferences/common/preferences';
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace';
import { IAction, IActionRunner } from 'vs/base/common/actions';
import { attachInputBoxStyler, attachStylerCallback, attachSelectBoxStyler } from 'vs/platform/theme/common/styler';
import { attachInputBoxStyler, attachStylerCallback, attachSelectBoxStyler, attachCheckboxStyler } from 'vs/platform/theme/common/styler';
import { IThemeService } from 'vs/platform/theme/common/themeService';
import { Position } from 'vs/editor/common/core/position';
import { ICursorPositionChangedEvent } from 'vs/editor/common/controller/cursorEvents';
@@ -32,19 +33,20 @@ import { ISelectBoxStyles, defaultStyles } from 'vs/base/browser/ui/selectBox/se
import { Separator } from 'vs/base/browser/ui/actionbar/actionbar';
import { Color } from 'vs/base/common/color';
import { SIDE_BAR_BACKGROUND } from 'vs/workbench/common/theme';
import { ConfigurationTarget } from 'vs/workbench/services/configuration/common/configurationEditing';
import { IMouseEvent } from 'vs/base/browser/mouseEvent';
import { MarkdownString } from 'vs/base/common/htmlContent';
import { ConfigurationTarget } from 'vs/platform/configuration/common/configuration';
import { IMarginData } from 'vs/editor/browser/controller/mouseTarget';
export class SettingsHeaderWidget extends Widget implements IViewZone {
private id: number;
private _domNode: HTMLElement;
private titleContainer: HTMLElement;
protected titleContainer: HTMLElement;
private messageElement: HTMLElement;
constructor(private editor: ICodeEditor, private title: string) {
constructor(protected editor: ICodeEditor, private title: string) {
super();
this.create();
this._register(this.editor.onDidChangeConfiguration(() => this.layout()));
@@ -63,7 +65,7 @@ export class SettingsHeaderWidget extends Widget implements IViewZone {
return 0;
}
private create() {
protected create() {
this._domNode = DOM.$('.settings-header-widget');
this.titleContainer = DOM.append(this._domNode, DOM.$('.title-container'));
@@ -101,6 +103,38 @@ 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-fuzzy-link'));
this.linkElement.textContent = localize('defaultSettingsFuzzyPrompt', "Try fuzzy search!");
this.onclick(this.linkElement, e => this._onClick.fire());
this.toggleMessage(true);
}
public toggleMessage(hasSettings: boolean, promptFuzzy = false): 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');
}
}
}
}
export class SettingsGroupTitleWidget extends Widget implements IViewZone {
private id: number;
@@ -271,7 +305,7 @@ export class SettingsTargetsWidget extends Widget {
private borderColor: Color;
constructor(parent: HTMLElement, private uri: URI, private target: ConfigurationTarget,
constructor(parent: HTMLElement, private _uri: URI, private _configuartionTarget: ConfigurationTarget,
@IWorkspaceContextService private workspaceContextService: IWorkspaceContextService,
@IPreferencesService private preferencesService: IPreferencesService,
@IContextMenuService private contextMenuService: IContextMenuService,
@@ -285,15 +319,19 @@ export class SettingsTargetsWidget extends Widget {
}));
}
public setTarget(uri: URI, target: ConfigurationTarget): void {
this.uri = uri;
this.target = target;
get configurationTarget(): ConfigurationTarget {
return this._configuartionTarget;
}
public updateTargets(uri: URI, configuartionTarget: ConfigurationTarget): void {
this._uri = uri;
this._configuartionTarget = configuartionTarget;
this.updateLabel();
}
private create(parent: HTMLElement): void {
this.settingsTargetsContainer = DOM.append(parent, DOM.$('.settings-targets-widget'));
this.settingsTargetsContainer.style.width = this.workspaceContextService.hasMultiFolderWorkspace() ? '200px' : '150px';
this.settingsTargetsContainer.style.width = this.workspaceContextService.getWorkbenchState() === WorkbenchState.WORKSPACE ? '200px' : '150px';
const targetElement = DOM.append(this.settingsTargetsContainer, DOM.$('.settings-target'));
this.targetLabel = DOM.append(targetElement, DOM.$('.settings-target-label'));
@@ -308,8 +346,8 @@ export class SettingsTargetsWidget extends Widget {
}
private updateLabel(): void {
this.targetLabel.textContent = getSettingsTargetName(this.target, this.uri, this.workspaceContextService);
const details = ConfigurationTarget.FOLDER === this.target ? localize('folderSettingsDetails', "Folder Settings") : '';
this.targetLabel.textContent = getSettingsTargetName(this._configuartionTarget, this._uri, this.workspaceContextService);
const details = ConfigurationTarget.WORKSPACE_FOLDER === this._configuartionTarget ? localize('folderSettingsDetails', "Folder Settings") : '';
this.targetDetails.textContent = details;
DOM.toggleClass(this.targetDetails, 'empty', !details);
}
@@ -332,31 +370,32 @@ export class SettingsTargetsWidget extends Widget {
actions.push(<IAction>{
id: 'userSettingsTarget',
label: getSettingsTargetName(ConfigurationTarget.USER, userSettingsResource, this.workspaceContextService),
checked: this.uri.fsPath === userSettingsResource.fsPath,
checked: this._uri.toString() === userSettingsResource.toString(),
enabled: true,
run: () => this.onTargetClicked(userSettingsResource)
});
if (this.workspaceContextService.hasWorkspace()) {
if (this.workspaceContextService.getWorkbenchState() !== WorkbenchState.EMPTY) {
const workspaceSettingsResource = this.preferencesService.workspaceSettingsResource;
actions.push(<IAction>{
id: 'workspaceSettingsTarget',
label: getSettingsTargetName(ConfigurationTarget.WORKSPACE, workspaceSettingsResource, this.workspaceContextService),
checked: this.uri.fsPath === workspaceSettingsResource.fsPath,
checked: this._uri.toString() === workspaceSettingsResource.toString(),
enabled: true,
run: () => this.onTargetClicked(workspaceSettingsResource)
});
}
if (this.workspaceContextService.hasMultiFolderWorkspace()) {
const workspaceFolders = this.workspaceContextService.getWorkspace().folders;
if (this.workspaceContextService.getWorkbenchState() === WorkbenchState.WORKSPACE && workspaceFolders.length > 0) {
actions.push(new Separator());
actions.push(...this.workspaceContextService.getWorkspace().roots.map((root, index) => {
actions.push(...workspaceFolders.map((folder, index) => {
return <IAction>{
id: 'folderSettingsTarget' + index,
label: getSettingsTargetName(ConfigurationTarget.FOLDER, root, this.workspaceContextService),
checked: this.uri.fsPath === root.fsPath,
label: getSettingsTargetName(ConfigurationTarget.WORKSPACE_FOLDER, folder.uri, this.workspaceContextService),
checked: this._uri.toString() === folder.uri.toString(),
enabled: true,
run: () => this.onTargetClicked(root)
run: () => this.onTargetClicked(folder.uri)
};
}));
}
@@ -365,7 +404,7 @@ export class SettingsTargetsWidget extends Widget {
}
private onTargetClicked(target: URI): void {
if (this.uri.fsPath === target.fsPath) {
if (this._uri.toString() === target.toString()) {
return;
}
this._onDidTargetChange.fire(target);
@@ -394,13 +433,12 @@ 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>());
public readonly onDidChange: Event<string> = this._onDidChange.event;
private _onNavigate: Emitter<boolean> = this._register(new Emitter<boolean>());
public readonly onNavigate: Event<boolean> = this._onNavigate.event;
private _onFocus: Emitter<void> = this._register(new Emitter<void>());
public readonly onFocus: Event<void> = this._onFocus.event;
@@ -414,10 +452,31 @@ 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.countElement = DOM.append(this.domNode, DOM.$('.settings-count-widget'));
this.controlsDiv = DOM.append(this.domNode, DOM.$('div.settings-search-controls'));
this.fuzzyToggle = this._register(new Checkbox({
actionClassName: 'prefs-fuzzy-search-toggle',
isChecked: false,
onChange: () => {
this.inputBox.focus();
this._onDidChange.fire();
},
title: localize('enableFuzzySearch', 'Enable experimental fuzzy search')
}));
DOM.append(this.controlsDiv, this.fuzzyToggle.domNode);
this._register(attachCheckboxStyler(this.fuzzyToggle, this.themeService));
this.countElement = DOM.append(this.controlsDiv, DOM.$('.settings-count-widget'));
this._register(attachStylerCallback(this.themeService, { badgeBackground, contrastBorder }, colors => {
const background = colors.badgeBackground ? colors.badgeBackground.toString() : null;
const border = colors.contrastBorder ? colors.contrastBorder.toString() : null;
@@ -446,7 +505,6 @@ export class SearchWidget extends Widget {
const searchInput = DOM.append(this.searchContainer, DOM.$('div.settings-search-input'));
this.inputBox = this._register(this.createInputBox(searchInput));
this._register(this.inputBox.onDidChange(value => this._onDidChange.fire(value)));
this.onkeydown(this.inputBox.inputElement, (e) => this._onKeyDown(e));
}
protected createInputBox(parent: HTMLElement): InputBox {
@@ -460,10 +518,20 @@ export class SearchWidget extends Widget {
this.countElement.textContent = message;
this.inputBox.inputElement.setAttribute('aria-label', message);
DOM.toggleClass(this.countElement, 'no-results', count === 0);
this.inputBox.inputElement.style.paddingRight = DOM.getTotalWidth(this.countElement) + 20 + 'px';
this.inputBox.inputElement.style.paddingRight = this.getControlsWidth() + 'px';
this.styleCountElementForeground();
}
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);
@@ -476,10 +544,14 @@ export class SearchWidget extends Widget {
this.inputBox.inputElement.style.paddingRight = '0px';
} else {
DOM.removeClass(this.countElement, 'hide');
this.inputBox.inputElement.style.paddingRight = DOM.getTotalWidth(this.countElement) + 20 + 'px';
this.inputBox.inputElement.style.paddingRight = this.getControlsWidth() + 'px';
}
}
private getControlsWidth(): number {
return DOM.getTotalWidth(this.countElement) + DOM.getTotalWidth(this.fuzzyToggle.domNode) + 20;
}
public focus() {
this.inputBox.focus();
if (this.getValue()) {
@@ -503,24 +575,6 @@ export class SearchWidget extends Widget {
return this.inputBox.value = value;
}
private _onKeyDown(keyboardEvent: IKeyboardEvent): void {
let handled = false;
switch (keyboardEvent.keyCode) {
case KeyCode.Enter:
this._onNavigate.fire(keyboardEvent.shiftKey);
handled = true;
break;
case KeyCode.Escape:
this.clear();
handled = true;
break;
}
if (handled) {
keyboardEvent.preventDefault();
keyboardEvent.stopPropagation();
}
}
public dispose(): void {
if (this.options.focusKey) {
this.options.focusKey.set(false);
@@ -602,7 +656,8 @@ export class EditPreferenceWidget<T> extends Disposable {
super();
this._editPreferenceDecoration = [];
this._register(this.editor.onMouseDown((e: IEditorMouseEvent) => {
if (e.target.type !== MouseTargetType.GUTTER_GLYPH_MARGIN || /* after last line */ e.target.detail || !this.isVisible()) {
const data = e.target.detail as IMarginData;
if (e.target.type !== MouseTargetType.GUTTER_GLYPH_MARGIN || data.isAfterLines || !this.isVisible()) {
return;
}
this._onClick.fire(e);