preferencesModels.ts 27.2 KB
Newer Older
S
Sandeep Somavarapu 已提交
1 2 3 4 5 6
/*---------------------------------------------------------------------------------------------
 *  Copyright (c) Microsoft Corporation. All rights reserved.
 *  Licensed under the MIT License. See License.txt in the project root for license information.
 *--------------------------------------------------------------------------------------------*/

import * as nls from 'vs/nls';
7
import { assign } from 'vs/base/common/objects';
8
import { tail } from 'vs/base/common/arrays';
S
Sandeep Somavarapu 已提交
9
import URI from 'vs/base/common/uri';
10
import { IReference, Disposable } from 'vs/base/common/lifecycle';
S
Sandeep Somavarapu 已提交
11
import Event, { Emitter } from 'vs/base/common/event';
12
import { Registry } from 'vs/platform/registry/common/platform';
13
import { visit, JSONVisitor } from 'vs/base/common/json';
A
Alex Dima 已提交
14
import { IModel } from 'vs/editor/common/editorCommon';
S
Sandeep Somavarapu 已提交
15
import { EditorModel } from 'vs/workbench/common/editor';
S
Sandeep Somavarapu 已提交
16
import { IConfigurationNode, IConfigurationRegistry, Extensions, OVERRIDE_PROPERTY_PATTERN, IConfigurationPropertySchema, ConfigurationScope } from 'vs/platform/configuration/common/configurationRegistry';
17
import { ISettingsEditorModel, IKeybindingsEditorModel, ISettingsGroup, ISetting, IFilterResult, ISettingsSection, IGroupFilter, ISettingMatcher } from 'vs/workbench/parts/preferences/common/preferences';
S
Sandeep Somavarapu 已提交
18
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
S
Sandeep Somavarapu 已提交
19
import { ITextEditorModel } from 'vs/editor/common/services/resolverService';
20
import { IRange, Range } from 'vs/editor/common/core/range';
21
import { ConfigurationTarget } from 'vs/platform/configuration/common/configuration';
S
Sandeep Somavarapu 已提交
22

S
Sandeep Somavarapu 已提交
23 24 25 26 27 28
export abstract class AbstractSettingsModel extends EditorModel {

	public get groupsTerms(): string[] {
		return this.settingsGroups.map(group => '@' + group.id);
	}

29
	protected doFilterSettings(filter: string, groupFilter: IGroupFilter, settingMatcher: ISettingMatcher): IFilterResult {
R
Rob Lourens 已提交
30 31
		const allGroups = this.settingsGroups;

S
Sandeep Somavarapu 已提交
32 33 34 35
		if (!filter) {
			return {
				filteredGroups: allGroups,
				allGroups,
36 37
				matches: [],
				query: filter
S
Sandeep Somavarapu 已提交
38 39 40 41 42 43 44 45
			};
		}

		const group = this.filterByGroupTerm(filter);
		if (group) {
			return {
				filteredGroups: [group],
				allGroups,
46 47
				matches: [],
				query: filter
S
Sandeep Somavarapu 已提交
48 49 50
			};
		}

51
		const matches: IRange[] = [];
S
Sandeep Somavarapu 已提交
52 53
		const filteredGroups: ISettingsGroup[] = [];
		for (const group of allGroups) {
R
Rob Lourens 已提交
54
			const groupMatched = groupFilter(group);
S
Sandeep Somavarapu 已提交
55 56 57 58
			const sections: ISettingsSection[] = [];
			for (const section of group.sections) {
				const settings: ISetting[] = [];
				for (const setting of section.settings) {
59
					const settingMatches = settingMatcher(setting);
R
Rob Lourens 已提交
60
					if (groupMatched || settingMatches && settingMatches.length) {
S
Sandeep Somavarapu 已提交
61 62
						settings.push(setting);
					}
R
Rob Lourens 已提交
63 64 65 66

					if (settingMatches) {
						matches.push(...settingMatches);
					}
S
Sandeep Somavarapu 已提交
67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85
				}
				if (settings.length) {
					sections.push({
						title: section.title,
						settings,
						titleRange: section.titleRange
					});
				}
			}
			if (sections.length) {
				filteredGroups.push({
					id: group.id,
					title: group.title,
					titleRange: group.titleRange,
					sections,
					range: group.range
				});
			}
		}
86
		return { filteredGroups, matches, allGroups, query: filter };
S
Sandeep Somavarapu 已提交
87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108
	}

	private filterByGroupTerm(filter: string): ISettingsGroup {
		if (this.groupsTerms.indexOf(filter) !== -1) {
			const id = filter.substring(1);
			return this.settingsGroups.filter(group => group.id === id)[0];
		}
		return null;
	}

	public getPreference(key: string): ISetting {
		for (const group of this.settingsGroups) {
			for (const section of group.sections) {
				for (const setting of section.settings) {
					if (key === setting.key) {
						return setting;
					}
				}
			}
		}
		return null;
	}
109

S
Sandeep Somavarapu 已提交
110
	public abstract settingsGroups: ISettingsGroup[];
111 112

	public abstract findValueMatches(filter: string, setting: ISetting): IRange[];
S
Sandeep Somavarapu 已提交
113 114 115
}

export class SettingsEditorModel extends AbstractSettingsModel implements ISettingsEditorModel {
116 117

	private _settingsGroups: ISettingsGroup[];
118
	protected settingsModel: IModel;
119

S
Sandeep Somavarapu 已提交
120 121 122
	private _onDidChangeGroups: Emitter<void> = this._register(new Emitter<void>());
	readonly onDidChangeGroups: Event<void> = this._onDidChangeGroups.event;

S
Sandeep Somavarapu 已提交
123
	constructor(reference: IReference<ITextEditorModel>, private _configurationTarget: ConfigurationTarget) {
124
		super();
125
		this.settingsModel = reference.object.textEditorModel;
S
Sandeep Somavarapu 已提交
126
		this._register(this.onDispose(() => reference.dispose()));
S
Sandeep Somavarapu 已提交
127 128 129 130
		this._register(this.settingsModel.onDidChangeContent(() => {
			this._settingsGroups = null;
			this._onDidChangeGroups.fire();
		}));
131 132 133
	}

	public get uri(): URI {
134
		return this.settingsModel.uri;
135 136 137 138 139 140 141 142 143 144 145 146 147 148
	}

	public get configurationTarget(): ConfigurationTarget {
		return this._configurationTarget;
	}

	public get settingsGroups(): ISettingsGroup[] {
		if (!this._settingsGroups) {
			this.parse();
		}
		return this._settingsGroups;
	}

	public get content(): string {
149
		return this.settingsModel.getValue();
150 151
	}

152 153
	public filterSettings(filter: string, groupFilter: IGroupFilter, settingMatcher: ISettingMatcher): IFilterResult {
		return this.doFilterSettings(filter, groupFilter, settingMatcher);
R
Rob Lourens 已提交
154 155 156 157
	}

	public findValueMatches(filter: string, setting: ISetting): IRange[] {
		return this.settingsModel.findMatches(filter, setting.valueRange, false, false, null, false).map(match => match.range);
S
Sandeep Somavarapu 已提交
158 159
	}

S
Sandeep Somavarapu 已提交
160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258
	protected isSettingsProperty(property: string, previousParents: string[]): boolean {
		return previousParents.length === 0; // Settings is root
	}

	protected parse(): void {
		this._settingsGroups = parse(this.settingsModel, (property: string, previousParents: string[]): boolean => this.isSettingsProperty(property, previousParents));
	}
}

function parse(model: IModel, isSettingsProperty: (currentProperty: string, previousParents: string[]) => boolean): ISettingsGroup[] {
	const settings: ISetting[] = [];
	let overrideSetting: ISetting = null;

	let currentProperty: string = null;
	let currentParent: any = [];
	let previousParents: any[] = [];
	let settingsPropertyIndex: number = -1;
	let range = {
		startLineNumber: 0,
		startColumn: 0,
		endLineNumber: 0,
		endColumn: 0
	};

	function onValue(value: any, offset: number, length: number) {
		if (Array.isArray(currentParent)) {
			(<any[]>currentParent).push(value);
		} else if (currentProperty) {
			currentParent[currentProperty] = value;
		}
		if (previousParents.length === settingsPropertyIndex + 1 || (previousParents.length === settingsPropertyIndex + 2 && overrideSetting !== null)) {
			// settings value started
			const setting = previousParents.length === settingsPropertyIndex + 1 ? settings[settings.length - 1] : overrideSetting.overrides[overrideSetting.overrides.length - 1];
			if (setting) {
				let valueStartPosition = model.getPositionAt(offset);
				let valueEndPosition = model.getPositionAt(offset + length);
				setting.value = value;
				setting.valueRange = {
					startLineNumber: valueStartPosition.lineNumber,
					startColumn: valueStartPosition.column,
					endLineNumber: valueEndPosition.lineNumber,
					endColumn: valueEndPosition.column
				};
				setting.range = assign(setting.range, {
					endLineNumber: valueEndPosition.lineNumber,
					endColumn: valueEndPosition.column
				});
			}
		}
	}
	let visitor: JSONVisitor = {
		onObjectBegin: (offset: number, length: number) => {
			if (isSettingsProperty(currentProperty, previousParents)) {
				// Settings started
				settingsPropertyIndex = previousParents.length;
				let position = model.getPositionAt(offset);
				range.startLineNumber = position.lineNumber;
				range.startColumn = position.column;
			}
			let object = {};
			onValue(object, offset, length);
			currentParent = object;
			currentProperty = null;
			previousParents.push(currentParent);
		},
		onObjectProperty: (name: string, offset: number, length: number) => {
			currentProperty = name;
			if (previousParents.length === settingsPropertyIndex + 1 || (previousParents.length === settingsPropertyIndex + 2 && overrideSetting !== null)) {
				// setting started
				let settingStartPosition = model.getPositionAt(offset);
				const setting: ISetting = {
					description: [],
					key: name,
					keyRange: {
						startLineNumber: settingStartPosition.lineNumber,
						startColumn: settingStartPosition.column + 1,
						endLineNumber: settingStartPosition.lineNumber,
						endColumn: settingStartPosition.column + length
					},
					range: {
						startLineNumber: settingStartPosition.lineNumber,
						startColumn: settingStartPosition.column,
						endLineNumber: 0,
						endColumn: 0
					},
					value: null,
					valueRange: null,
					descriptionRanges: null,
					overrides: [],
					overrideOf: overrideSetting
				};
				if (previousParents.length === settingsPropertyIndex + 1) {
					settings.push(setting);
					if (OVERRIDE_PROPERTY_PATTERN.test(name)) {
						overrideSetting = setting;
					}
				} else {
					overrideSetting.overrides.push(setting);
				}
259
			}
S
Sandeep Somavarapu 已提交
260 261 262 263 264 265
		},
		onObjectEnd: (offset: number, length: number) => {
			currentParent = previousParents.pop();
			if (previousParents.length === settingsPropertyIndex + 1 || (previousParents.length === settingsPropertyIndex + 2 && overrideSetting !== null)) {
				// setting ended
				const setting = previousParents.length === settingsPropertyIndex + 1 ? settings[settings.length - 1] : overrideSetting.overrides[overrideSetting.overrides.length - 1];
S
Sandeep Somavarapu 已提交
266 267
				if (setting) {
					let valueEndPosition = model.getPositionAt(offset + length);
S
Sandeep Somavarapu 已提交
268
					setting.valueRange = assign(setting.valueRange, {
S
Sandeep Somavarapu 已提交
269 270
						endLineNumber: valueEndPosition.lineNumber,
						endColumn: valueEndPosition.column
S
Sandeep Somavarapu 已提交
271
					});
S
Sandeep Somavarapu 已提交
272 273 274 275 276
					setting.range = assign(setting.range, {
						endLineNumber: valueEndPosition.lineNumber,
						endColumn: valueEndPosition.column
					});
				}
277

S
Sandeep Somavarapu 已提交
278 279
				if (previousParents.length === settingsPropertyIndex + 1) {
					overrideSetting = null;
280
				}
S
Sandeep Somavarapu 已提交
281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310
			}
			if (previousParents.length === settingsPropertyIndex) {
				// settings ended
				let position = model.getPositionAt(offset);
				range.endLineNumber = position.lineNumber;
				range.endColumn = position.column;
			}
		},
		onArrayBegin: (offset: number, length: number) => {
			let array: any[] = [];
			onValue(array, offset, length);
			previousParents.push(currentParent);
			currentParent = array;
			currentProperty = null;
		},
		onArrayEnd: (offset: number, length: number) => {
			currentParent = previousParents.pop();
			if (previousParents.length === settingsPropertyIndex + 1 || (previousParents.length === settingsPropertyIndex + 2 && overrideSetting !== null)) {
				// setting value ended
				const setting = previousParents.length === settingsPropertyIndex + 1 ? settings[settings.length - 1] : overrideSetting.overrides[overrideSetting.overrides.length - 1];
				if (setting) {
					let valueEndPosition = model.getPositionAt(offset + length);
					setting.valueRange = assign(setting.valueRange, {
						endLineNumber: valueEndPosition.lineNumber,
						endColumn: valueEndPosition.column
					});
					setting.range = assign(setting.range, {
						endLineNumber: valueEndPosition.lineNumber,
						endColumn: valueEndPosition.column
					});
S
#17292  
Sandeep Somavarapu 已提交
311
				}
312
			}
S
Sandeep Somavarapu 已提交
313 314 315 316 317 318 319
		},
		onLiteralValue: onValue,
		onError: (error) => {
			const setting = settings[settings.length - 1];
			if (setting && (!setting.range || !setting.keyRange || !setting.valueRange)) {
				settings.pop();
			}
S
Sandeep Somavarapu 已提交
320
		}
S
Sandeep Somavarapu 已提交
321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347
	};
	if (!model.isDisposed()) {
		visit(model.getValue(), visitor);
	}
	return settings.length > 0 ? [<ISettingsGroup>{
		sections: [
			{
				settings
			}
		],
		title: null,
		titleRange: null,
		range
	}] : [];
}

export class WorkspaceConfigurationEditorModel extends SettingsEditorModel {

	private _configurationGroups: ISettingsGroup[];

	get configurationGroups(): ISettingsGroup[] {
		return this._configurationGroups;
	}

	protected parse(): void {
		super.parse();
		this._configurationGroups = parse(this.settingsModel, (property: string, previousParents: string[]): boolean => previousParents.length === 0);
348
	}
S
Sandeep Somavarapu 已提交
349 350 351 352 353

	protected isSettingsProperty(property: string, previousParents: string[]): boolean {
		return property === 'settings' && previousParents.length === 1;
	}

354 355
}

356
export class DefaultSettings extends Disposable {
R
Rob Lourens 已提交
357

S
Sandeep Somavarapu 已提交
358 359
	private static _RAW: string;

S
Sandeep Somavarapu 已提交
360
	private _allSettingsGroups: ISettingsGroup[];
361
	private _content: string;
362
	private _settingsByName: Map<string, ISetting>;
363

364
	readonly _onDidChange: Emitter<void> = this._register(new Emitter<void>());
365 366
	readonly onDidChange: Event<void> = this._onDidChange.event;

367 368 369
	constructor(
		private _mostCommonlyUsedSettingsKeys: string[],
		readonly configurationScope: ConfigurationScope,
370
	) {
371
		super();
S
Sandeep Somavarapu 已提交
372 373
	}

S
Sandeep Somavarapu 已提交
374
	get content(): string {
375
		if (!this._content) {
376
			this.parse();
377
		}
S
Sandeep Somavarapu 已提交
378 379 380
		return this._content;
	}

S
Sandeep Somavarapu 已提交
381
	get settingsGroups(): ISettingsGroup[] {
S
Sandeep Somavarapu 已提交
382
		if (!this._allSettingsGroups) {
383
			this.parse();
384
		}
S
Sandeep Somavarapu 已提交
385
		return this._allSettingsGroups;
S
Sandeep Somavarapu 已提交
386 387
	}

S
Sandeep Somavarapu 已提交
388
	parse(): string {
S
Sandeep Somavarapu 已提交
389
		const settingsGroups = this.getRegisteredGroups();
390
		this.initAllSettingsMap(settingsGroups);
S
Sandeep Somavarapu 已提交
391
		const mostCommonlyUsed = this.getMostCommonlyUsedSettings(settingsGroups);
392
		this._allSettingsGroups = [mostCommonlyUsed, ...settingsGroups];
393
		this._content = this.toContent(this._allSettingsGroups, true);
S
Sandeep Somavarapu 已提交
394
		return this._content;
395 396
	}

S
Sandeep Somavarapu 已提交
397 398
	get raw(): string {
		if (!DefaultSettings._RAW) {
399
			DefaultSettings._RAW = this.toContent(this.getRegisteredGroups(), false);
S
Sandeep Somavarapu 已提交
400 401 402 403 404 405 406 407 408 409
		}
		return DefaultSettings._RAW;
	}

	private getRegisteredGroups(): ISettingsGroup[] {
		const configurations = Registry.as<IConfigurationRegistry>(Extensions.Configuration).getConfigurations().slice();
		return this.removeEmptySettingsGroups(configurations.sort(this.compareConfigurationNodes)
			.reduce((result, config, index, array) => this.parseConfig(config, result, array), []));
	}

410 411
	private initAllSettingsMap(allSettingsGroups: ISettingsGroup[]): void {
		this._settingsByName = new Map<string, ISetting>();
S
Sandeep Somavarapu 已提交
412 413 414
		for (const group of allSettingsGroups) {
			for (const section of group.sections) {
				for (const setting of section.settings) {
415
					this._settingsByName.set(setting.key, setting);
S
Sandeep Somavarapu 已提交
416 417 418
				}
			}
		}
419 420 421
	}

	private getMostCommonlyUsedSettings(allSettingsGroups: ISettingsGroup[]): ISettingsGroup {
S
Sandeep Somavarapu 已提交
422
		const settings = this._mostCommonlyUsedSettingsKeys.map(key => {
423
			const setting = this._settingsByName.get(key);
S
Sandeep Somavarapu 已提交
424 425 426 427 428 429
			if (setting) {
				return <ISetting>{
					description: setting.description,
					key: setting.key,
					value: setting.value,
					range: null,
430
					valueRange: null,
431
					overrides: []
S
Sandeep Somavarapu 已提交
432 433 434 435 436 437 438 439
				};
			}
			return null;
		}).filter(setting => !!setting);

		return <ISettingsGroup>{
			id: 'mostCommonlyUsed',
			range: null,
440
			title: nls.localize('commonlyUsed', "Commonly Used"),
S
Sandeep Somavarapu 已提交
441 442 443 444 445 446 447 448 449
			titleRange: null,
			sections: [
				{
					settings
				}
			]
		};
	}

S
Sandeep Somavarapu 已提交
450 451 452 453 454 455 456 457 458
	private parseConfig(config: IConfigurationNode, result: ISettingsGroup[], configurations: IConfigurationNode[], settingsGroup?: ISettingsGroup): ISettingsGroup[] {
		let title = config.title;
		if (!title) {
			const configWithTitleAndSameId = configurations.filter(c => c.id === config.id && c.title)[0];
			if (configWithTitleAndSameId) {
				title = configWithTitleAndSameId.title;
			}
		}
		if (title) {
S
Sandeep Somavarapu 已提交
459
			if (!settingsGroup) {
S
Sandeep Somavarapu 已提交
460
				settingsGroup = result.filter(g => g.title === title)[0];
S
Sandeep Somavarapu 已提交
461
				if (!settingsGroup) {
S
Sandeep Somavarapu 已提交
462
					settingsGroup = { sections: [{ settings: [] }], id: config.id, title: title, titleRange: null, range: null };
S
Sandeep Somavarapu 已提交
463 464 465
					result.push(settingsGroup);
				}
			} else {
S
Sandeep Somavarapu 已提交
466
				settingsGroup.sections[settingsGroup.sections.length - 1].title = title;
S
Sandeep Somavarapu 已提交
467 468 469 470
			}
		}
		if (config.properties) {
			if (!settingsGroup) {
S
Sandeep Somavarapu 已提交
471
				settingsGroup = { sections: [{ settings: [] }], id: config.id, title: config.id, titleRange: null, range: null };
S
Sandeep Somavarapu 已提交
472 473
				result.push(settingsGroup);
			}
S
Sandeep Somavarapu 已提交
474
			const configurationSettings: ISetting[] = [...settingsGroup.sections[settingsGroup.sections.length - 1].settings, ...this.parseSettings(config.properties)];
475
			if (configurationSettings.length) {
S
Sandeep Somavarapu 已提交
476 477
				configurationSettings.sort((a, b) => a.key.localeCompare(b.key));
				settingsGroup.sections[settingsGroup.sections.length - 1].settings = configurationSettings;
478
			}
S
Sandeep Somavarapu 已提交
479 480
		}
		if (config.allOf) {
S
Sandeep Somavarapu 已提交
481
			config.allOf.forEach(c => this.parseConfig(c, result, configurations, settingsGroup));
S
Sandeep Somavarapu 已提交
482 483 484 485
		}
		return result;
	}

S
Sandeep Somavarapu 已提交
486 487 488 489 490 491 492 493 494 495 496
	private removeEmptySettingsGroups(settingsGroups: ISettingsGroup[]): ISettingsGroup[] {
		const result = [];
		for (const settingsGroup of settingsGroups) {
			settingsGroup.sections = settingsGroup.sections.filter(section => section.settings.length > 0);
			if (settingsGroup.sections.length) {
				result.push(settingsGroup);
			}
		}
		return result;
	}

497 498 499
	private parseSettings(settingsObject: { [path: string]: IConfigurationPropertySchema; }): ISetting[] {
		let result = [];
		for (let key in settingsObject) {
500
			const prop = settingsObject[key];
S
Sandeep Somavarapu 已提交
501
			if (!prop.deprecationMessage && this.matchesScope(prop)) {
502 503 504 505 506 507 508
				const value = prop.default;
				const description = (prop.description || '').split('\n');
				const overrides = OVERRIDE_PROPERTY_PATTERN.test(key) ? this.parseOverrideSettings(prop.default) : [];
				result.push({ key, value, description, range: null, keyRange: null, valueRange: null, descriptionRanges: [], overrides });
			}
		}
		return result;
509 510 511 512 513 514
	}

	private parseOverrideSettings(overrideSettings: any): ISetting[] {
		return Object.keys(overrideSettings).map((key) => ({ key, value: overrideSettings[key], description: [], range: null, keyRange: null, valueRange: null, descriptionRanges: [], overrides: [] }));
	}

S
Sandeep Somavarapu 已提交
515
	private matchesScope(property: IConfigurationNode): boolean {
S
Sandeep Somavarapu 已提交
516
		if (this.configurationScope === ConfigurationScope.WINDOW) {
S
Sandeep Somavarapu 已提交
517 518 519 520 521
			return true;
		}
		return property.scope === this.configurationScope;
	}

S
Sandeep Somavarapu 已提交
522 523 524 525 526 527 528 529 530 531 532 533 534 535 536
	private compareConfigurationNodes(c1: IConfigurationNode, c2: IConfigurationNode): number {
		if (typeof c1.order !== 'number') {
			return 1;
		}
		if (typeof c2.order !== 'number') {
			return -1;
		}
		if (c1.order === c2.order) {
			const title1 = c1.title || '';
			const title2 = c2.title || '';
			return title1.localeCompare(title2);
		}
		return c1.order - c2.order;
	}

537 538 539 540 541 542 543 544 545 546 547 548
	private toContent(settingsGroups: ISettingsGroup[], asArray: boolean): string {
		const builder = new SettingsContentBuilder();
		if (asArray) {
			builder.pushLine('[');
		}
		builder.pushGroups(settingsGroups);
		if (asArray) {
			builder.pushLine(']');
		}
		return builder.getContent();
	}

549 550 551 552
}

export class DefaultSettingsEditorModel extends AbstractSettingsModel implements ISettingsEditorModel {

553
	private _model: IModel;
554
	private _settingsByName: Map<string, ISetting>;
S
Sandeep Somavarapu 已提交
555 556 557 558

	private _onDidChangeGroups: Emitter<void> = this._register(new Emitter<void>());
	readonly onDidChangeGroups: Event<void> = this._onDidChangeGroups.event;

559 560 561 562
	constructor(
		private _uri: URI,
		reference: IReference<ITextEditorModel>,
		readonly configurationScope: ConfigurationScope,
563
		private readonly defaultSettings: DefaultSettings
564 565
	) {
		super();
S
Sandeep Somavarapu 已提交
566

567
		this._register(defaultSettings.onDidChange(() => this._onDidChangeGroups.fire()));
568
		this._model = reference.object.textEditorModel;
569 570 571 572 573 574 575 576 577
		this._register(this.onDispose(() => reference.dispose()));

		this.initAllSettingsMap();
	}

	public get uri(): URI {
		return this._uri;
	}

S
Sandeep Somavarapu 已提交
578
	public get settingsGroups(): ISettingsGroup[] {
579
		return this.defaultSettings.settingsGroups;
S
Sandeep Somavarapu 已提交
580 581
	}

582
	public filterSettings(filter: string, groupFilter: IGroupFilter, settingMatcher: ISettingMatcher, mostRelevantSettings?: string[]): IFilterResult {
583
		if (mostRelevantSettings) {
584
			const mostRelevantGroup = this.renderMostRelevantSettings(mostRelevantSettings);
585

586 587 588 589 590
			// calculate match ranges
			const matches = mostRelevantGroup.sections[0].settings.reduce((prev, s) => {
				return prev.concat(settingMatcher(s));
			}, []);

591
			return {
592 593
				allGroups: [...this.settingsGroups, mostRelevantGroup],
				filteredGroups: mostRelevantGroup.sections[0].settings.length ? [mostRelevantGroup] : [],
594
				matches,
595 596 597
				query: filter
			};
		} else {
598 599
			// Do local search and add empty 'most relevant' group
			const mostRelevantGroup = this.renderMostRelevantSettings([]);
600
			const result = this.doFilterSettings(filter, groupFilter, settingMatcher);
601 602
			result.allGroups = [...result.allGroups, mostRelevantGroup];
			return result;
603 604 605
		}
	}

606
	private renderMostRelevantSettings(mostRelevantSettings: string[]): ISettingsGroup {
607 608
		const mostRelevantLineOffset = tail(this.settingsGroups).range.endLineNumber + 2;
		const builder = new SettingsContentBuilder(mostRelevantLineOffset - 1);
609 610 611 612 613 614 615 616 617 618 619 620
		builder.pushLine(',');
		const mostRelevantGroup = this.getMostRelevantSettings(mostRelevantSettings);
		builder.pushGroups([mostRelevantGroup]);
		builder.pushLine('');

		// note: 1-indexed line numbers here
		const mostRelevantContent = builder.getContent();
		const mostRelevantEndLine = this._model.getLineCount();
		this._model.applyEdits([
			{
				text: mostRelevantContent,
				forceMoveMarkers: false,
621
				range: new Range(mostRelevantLineOffset, 1, mostRelevantEndLine, 1),
622 623 624 625 626 627 628
				identifier: { major: 1, minor: 0 }
			}
		]);

		return mostRelevantGroup;
	}

629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684
	public findValueMatches(filter: string, setting: ISetting): IRange[] {
		return [];
	}

	public getPreference(key: string): ISetting {
		for (const group of this.settingsGroups) {
			for (const section of group.sections) {
				for (const setting of section.settings) {
					if (setting.key === key) {
						return setting;
					}
				}
			}
		}
		return null;
	}

	private initAllSettingsMap(): void {
		this._settingsByName = new Map<string, ISetting>();
		for (const group of this.settingsGroups) {
			for (const section of group.sections) {
				for (const setting of section.settings) {
					this._settingsByName.set(setting.key, setting);
				}
			}
		}
	}

	private getMostRelevantSettings(rankedSettingNames: string[]): ISettingsGroup {
		const settings = rankedSettingNames.map(key => {
			const setting = this._settingsByName.get(key);
			if (setting) {
				return <ISetting>{
					description: setting.description,
					key: setting.key,
					value: setting.value,
					range: null,
					valueRange: null,
					overrides: []
				};
			}
			return null;
		}).filter(setting => !!setting);

		return <ISettingsGroup>{
			id: 'mostRelevant',
			range: null,
			title: nls.localize('mostRelevant', "Most Relevant"),
			titleRange: null,
			sections: [
				{
					settings
				}
			]
		};
	}
685 686 687 688 689
}

class SettingsContentBuilder {
	private _contentByLines: string[];

690 691
	get lines(): string[] {
		return this._contentByLines;
692 693 694 695 696 697
	}

	private get lineCountWithOffset(): number {
		return this._contentByLines.length + this._rangeOffset;
	}

698 699 700 701
	private get lastLine(): string {
		return this._contentByLines[this._contentByLines.length - 1] || '';
	}

S
Sandeep Somavarapu 已提交
702
	constructor(private _rangeOffset = 0) {
703 704 705
		this._contentByLines = [];
	}

706 707 708 709
	private offsetIndexToIndex(offsetIdx: number): number {
		return offsetIdx - this._rangeOffset;
	}

710 711 712 713 714
	pushLine(...lineText: string[]): void {
		this._contentByLines.push(...lineText);
	}

	pushGroups(settingsGroups: ISettingsGroup[]): void {
S
Sandeep Somavarapu 已提交
715
		let lastSetting: ISetting = null;
716
		this._contentByLines.push('{');
S
Sandeep Somavarapu 已提交
717
		this._contentByLines.push('');
718
		for (const group of settingsGroups) {
719
			this._contentByLines.push('');
S
Sandeep Somavarapu 已提交
720
			lastSetting = this.pushGroup(group);
S
Sandeep Somavarapu 已提交
721
		}
722
		if (lastSetting) {
723 724 725 726
			// Strip the comma from the last setting
			const lineIdx = this.offsetIndexToIndex(lastSetting.range.endLineNumber);
			const content = this._contentByLines[lineIdx - 2];
			this._contentByLines[lineIdx - 2] = content.substring(0, content.length - 1);
727
		}
728
		this._contentByLines.push('}');
S
Sandeep Somavarapu 已提交
729 730
	}

R
Rob Lourens 已提交
731
	private pushGroup(group: ISettingsGroup): ISetting {
732
		const indent = '  ';
S
Sandeep Somavarapu 已提交
733
		let lastSetting: ISetting = null;
734
		let groupStart = this.lineCountWithOffset + 1;
S
Sandeep Somavarapu 已提交
735
		for (const section of group.sections) {
736
			if (section.title) {
737
				let sectionTitleStart = this.lineCountWithOffset + 1;
738
				this.addDescription([section.title], indent, this._contentByLines);
739
				section.titleRange = { startLineNumber: sectionTitleStart, startColumn: 1, endLineNumber: this.lineCountWithOffset, endColumn: this.lastLine.length };
S
Sandeep Somavarapu 已提交
740 741
			}

742
			if (section.settings.length) {
S
Sandeep Somavarapu 已提交
743 744 745 746
				for (const setting of section.settings) {
					this.pushSetting(setting, indent);
					lastSetting = setting;
				}
S
Sandeep Somavarapu 已提交
747
			}
S
Sandeep Somavarapu 已提交
748

S
Sandeep Somavarapu 已提交
749
		}
750
		group.range = { startLineNumber: groupStart, startColumn: 1, endLineNumber: this.lineCountWithOffset, endColumn: this.lastLine.length };
S
Sandeep Somavarapu 已提交
751
		return lastSetting;
752
	}
S
Sandeep Somavarapu 已提交
753

754 755 756 757
	getContent(): string {
		return this._contentByLines.join('\n');
	}

758
	private pushSetting(setting: ISetting, indent: string): void {
759
		const settingStart = this.lineCountWithOffset + 1;
760 761 762 763
		setting.descriptionRanges = [];
		const descriptionPreValue = indent + '// ';
		for (const line of setting.description) {
			this._contentByLines.push(descriptionPreValue + line);
764
			setting.descriptionRanges.push({ startLineNumber: this.lineCountWithOffset, startColumn: this.lastLine.indexOf(line) + 1, endLineNumber: this.lineCountWithOffset, endColumn: this.lastLine.length });
765 766 767 768 769
		}

		let preValueConent = indent;
		const keyString = JSON.stringify(setting.key);
		preValueConent += keyString;
770
		setting.keyRange = { startLineNumber: this.lineCountWithOffset + 1, startColumn: preValueConent.indexOf(setting.key) + 1, endLineNumber: this.lineCountWithOffset + 1, endColumn: setting.key.length };
771 772

		preValueConent += ': ';
773
		const valueStart = this.lineCountWithOffset + 1;
774 775
		this.pushValue(setting, preValueConent, indent);

776
		setting.valueRange = { startLineNumber: valueStart, startColumn: preValueConent.length + 1, endLineNumber: this.lineCountWithOffset, endColumn: this.lastLine.length + 1 };
777 778
		this._contentByLines[this._contentByLines.length - 1] += ',';
		this._contentByLines.push('');
779
		setting.range = { startLineNumber: settingStart, startColumn: 1, endLineNumber: this.lineCountWithOffset, endColumn: this.lastLine.length };
780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806
	}

	private pushValue(setting: ISetting, preValueConent: string, indent: string): void {
		let valueString = JSON.stringify(setting.value, null, indent);
		if (valueString && (typeof setting.value === 'object')) {
			if (setting.overrides.length) {
				this._contentByLines.push(preValueConent + ' {');
				for (const subSetting of setting.overrides) {
					this.pushSetting(subSetting, indent + indent);
					this._contentByLines.pop();
				}
				const lastSetting = setting.overrides[setting.overrides.length - 1];
				const content = this._contentByLines[lastSetting.range.endLineNumber - 2];
				this._contentByLines[lastSetting.range.endLineNumber - 2] = content.substring(0, content.length - 1);
				this._contentByLines.push(indent + '}');
			} else {
				const mulitLineValue = valueString.split('\n');
				this._contentByLines.push(preValueConent + mulitLineValue[0]);
				for (let i = 1; i < mulitLineValue.length; i++) {
					this._contentByLines.push(indent + mulitLineValue[i]);
				}
			}
		} else {
			this._contentByLines.push(preValueConent + valueString);
		}
	}

807 808
	private addDescription(description: string[], indent: string, result: string[]) {
		for (const line of description) {
S
Sandeep Somavarapu 已提交
809
			result.push(indent + '// ' + line);
810
		}
S
Sandeep Somavarapu 已提交
811 812 813
	}
}

814 815 816 817 818
export function defaultKeybindingsContents(keybindingService: IKeybindingService): string {
	const defaultsHeader = '// ' + nls.localize('defaultKeybindingsHeader', "Overwrite key bindings by placing them into your key bindings file.");
	return defaultsHeader + '\n' + keybindingService.getDefaultKeybindingsContent();
}

819
export class DefaultKeybindingsEditorModel implements IKeybindingsEditorModel<any> {
S
Sandeep Somavarapu 已提交
820 821 822

	private _content: string;

823
	constructor(private _uri: URI,
S
Sandeep Somavarapu 已提交
824
		@IKeybindingService private keybindingService: IKeybindingService) {
S
Sandeep Somavarapu 已提交
825 826 827 828 829 830 831
	}

	public get uri(): URI {
		return this._uri;
	}

	public get content(): string {
832
		if (!this._content) {
833
			this._content = defaultKeybindingsContents(this.keybindingService);
834
		}
S
Sandeep Somavarapu 已提交
835 836
		return this._content;
	}
837 838 839 840

	public getPreference(): any {
		return null;
	}
S
Sandeep Somavarapu 已提交
841 842 843 844

	public dispose(): void {
		// Not disposable
	}
S
Sandeep Somavarapu 已提交
845
}