From 8d666f34e6caff28ca1a1c983b78c3a662d0e319 Mon Sep 17 00:00:00 2001 From: Ryan Stringham Date: Mon, 29 May 2017 15:54:30 -0600 Subject: [PATCH 001/136] Add history navigation for file includes and excludes patterns in the search viewlet --- .../search/browser/patternInputWidget.ts | 31 +++++++++ .../search/browser/search.contribution.ts | 40 ++++++++++++ .../parts/search/browser/searchActions.ts | 64 +++++++++++++++++++ .../parts/search/browser/searchViewlet.ts | 24 ++++++- .../parts/search/common/constants.ts | 2 + 5 files changed, 158 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/parts/search/browser/patternInputWidget.ts b/src/vs/workbench/parts/search/browser/patternInputWidget.ts index 17487587812..5c071d58847 100644 --- a/src/vs/workbench/parts/search/browser/patternInputWidget.ts +++ b/src/vs/workbench/parts/search/browser/patternInputWidget.ts @@ -20,6 +20,7 @@ import CommonEvent, { Emitter } from 'vs/base/common/event'; import { IThemeService } from 'vs/platform/theme/common/themeService'; import { attachInputBoxStyler, attachCheckboxStyler } from 'vs/platform/theme/common/styler'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { HistoryNavigator } from 'vs/base/common/history'; export interface IOptions { placeholder?: string; @@ -45,11 +46,14 @@ export class PatternInputWidget extends Widget { private inputNode: HTMLInputElement; protected inputBox: InputBox; + private history: HistoryNavigator; + private _onSubmit = this._register(new Emitter()); public onSubmit: CommonEvent = this._onSubmit.event; constructor(parent: HTMLElement, private contextViewProvider: IContextViewProvider, protected themeService: IThemeService, options: IOptions = Object.create(null)) { super(); + this.history = new HistoryNavigator(); this.onOptionChange = null; this.width = options.width || 100; this.placeholder = options.placeholder || ''; @@ -185,6 +189,26 @@ export class PatternInputWidget extends Widget { return this.pattern.width(); } + public showNextTerm() { + let next = this.history.next(); + if (next) { + this.setValue(next); + } + } + + public showPreviousTerm() { + let previous; + if (this.getValue().length === 0) { + previous = this.history.current(); + } else { + this.history.addIfNotPresent(this.getValue()); + previous = this.history.previous(); + } + if (previous) { + this.setValue(previous); + } + } + private render(): void { this.domNode = document.createElement('div'); this.domNode.style.width = this.width + 'px'; @@ -221,6 +245,13 @@ export class PatternInputWidget extends Widget { }); this._register(attachCheckboxStyler(this.pattern, this.themeService)); + this._register(this.onSubmit(() => { + let value = this.getValue(); + if (value.length > 0) { + this.history.add(this.getValue()); + } + })); + $(this.pattern.domNode).on('mouseover', () => { if (this.isGlobPattern()) { this.showGlobHelp(); diff --git a/src/vs/workbench/parts/search/browser/search.contribution.ts b/src/vs/workbench/parts/search/browser/search.contribution.ts index 2627a97b70e..26d50d5eb30 100644 --- a/src/vs/workbench/parts/search/browser/search.contribution.ts +++ b/src/vs/workbench/parts/search/browser/search.contribution.ts @@ -140,6 +140,46 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({ } }); +KeybindingsRegistry.registerCommandAndKeybindingRule({ + id: searchActions.ShowNextSearchIncludeAction.ID, + weight: KeybindingsRegistry.WEIGHT.workbenchContrib(), + when: ContextKeyExpr.and(Constants.SearchViewletVisibleKey, Constants.PatternIncludesFocussedKey), + primary: ShowNextFindTermKeybinding.primary, + handler: (accessor, args: any) => { + accessor.get(IInstantiationService).createInstance(searchActions.ShowNextSearchIncludeAction, searchActions.ShowNextSearchIncludeAction.ID, '').run(); + } +}); + +KeybindingsRegistry.registerCommandAndKeybindingRule({ + id: searchActions.ShowPreviousSearchIncludeAction.ID, + weight: KeybindingsRegistry.WEIGHT.workbenchContrib(), + when: ContextKeyExpr.and(Constants.SearchViewletVisibleKey, Constants.PatternIncludesFocussedKey), + primary: ShowPreviousFindTermKeybinding.primary, + handler: (accessor, args: any) => { + accessor.get(IInstantiationService).createInstance(searchActions.ShowPreviousSearchIncludeAction, searchActions.ShowPreviousSearchIncludeAction.ID, '').run(); + } +}); + +KeybindingsRegistry.registerCommandAndKeybindingRule({ + id: searchActions.ShowNextSearchExcludeAction.ID, + weight: KeybindingsRegistry.WEIGHT.workbenchContrib(), + when: ContextKeyExpr.and(Constants.SearchViewletVisibleKey, Constants.PatternExcludesFocussedKey), + primary: ShowNextFindTermKeybinding.primary, + handler: (accessor, args: any) => { + accessor.get(IInstantiationService).createInstance(searchActions.ShowNextSearchExcludeAction, searchActions.ShowNextSearchExcludeAction.ID, '').run(); + } +}); + +KeybindingsRegistry.registerCommandAndKeybindingRule({ + id: searchActions.ShowPreviousSearchExcludeAction.ID, + weight: KeybindingsRegistry.WEIGHT.workbenchContrib(), + when: ContextKeyExpr.and(Constants.SearchViewletVisibleKey, Constants.PatternExcludesFocussedKey), + primary: ShowPreviousFindTermKeybinding.primary, + handler: (accessor, args: any) => { + accessor.get(IInstantiationService).createInstance(searchActions.ShowPreviousSearchExcludeAction, searchActions.ShowPreviousSearchExcludeAction.ID, '').run(); + } +}); + KeybindingsRegistry.registerCommandAndKeybindingRule({ id: searchActions.ShowNextSearchTermAction.ID, weight: KeybindingsRegistry.WEIGHT.workbenchContrib(), diff --git a/src/vs/workbench/parts/search/browser/searchActions.ts b/src/vs/workbench/parts/search/browser/searchActions.ts index 346ecc21b54..5c9663585fb 100644 --- a/src/vs/workbench/parts/search/browser/searchActions.ts +++ b/src/vs/workbench/parts/search/browser/searchActions.ts @@ -88,6 +88,70 @@ export class ToggleRegexAction extends Action { } } +export class ShowNextSearchIncludeAction extends Action { + + public static ID = 'search.history.showNextIncludePattern'; + public static LABEL = nls.localize('nextSearchIncludePattern', "Show Next Search Include Pattern"); + + constructor(id: string, label: string, @IViewletService private viewletService: IViewletService) { + super(id, label); + } + + public run(): TPromise { + let searchAndReplaceWidget = (this.viewletService.getActiveViewlet()).searchIncludePattern; + searchAndReplaceWidget.showNextTerm(); + return TPromise.as(null); + } +} + +export class ShowPreviousSearchIncludeAction extends Action { + + public static ID = 'search.history.showPreviousIncludePattern'; + public static LABEL = nls.localize('previousSearchIncludePattern', "Show Previous Search Include Pattern"); + + constructor(id: string, label: string, @IViewletService private viewletService: IViewletService) { + super(id, label); + } + + public run(): TPromise { + let searchAndReplaceWidget = (this.viewletService.getActiveViewlet()).searchIncludePattern; + searchAndReplaceWidget.showPreviousTerm(); + return TPromise.as(null); + } +} + +export class ShowNextSearchExcludeAction extends Action { + + public static ID = 'search.history.showNextExcludePattern'; + public static LABEL = nls.localize('nextSearchExcludePattern', "Show Next Search Exclude Pattern"); + + constructor(id: string, label: string, @IViewletService private viewletService: IViewletService) { + super(id, label); + } + + public run(): TPromise { + let searchAndReplaceWidget = (this.viewletService.getActiveViewlet()).searchExcludePattern; + searchAndReplaceWidget.showNextTerm(); + return TPromise.as(null); + } +} + +export class ShowPreviousSearchExcludeAction extends Action { + + public static ID = 'search.history.showPreviousExcludePattern'; + public static LABEL = nls.localize('previousSearchExcludePattern', "Show Previous Search Exclude Pattern"); + + constructor(id: string, label: string, @IViewletService private viewletService: IViewletService) { + super(id, label); + } + + public run(): TPromise { + let searchAndReplaceWidget = (this.viewletService.getActiveViewlet()).searchExcludePattern; + searchAndReplaceWidget.showPreviousTerm(); + return TPromise.as(null); + } +} + export class ShowNextSearchTermAction extends Action { public static ID = 'search.history.showNext'; diff --git a/src/vs/workbench/parts/search/browser/searchViewlet.ts b/src/vs/workbench/parts/search/browser/searchViewlet.ts index 1aaebaee816..2e73a0cf36a 100644 --- a/src/vs/workbench/parts/search/browser/searchViewlet.ts +++ b/src/vs/workbench/parts/search/browser/searchViewlet.ts @@ -78,6 +78,8 @@ export class SearchViewlet extends Viewlet { private viewletVisible: IContextKey; private inputBoxFocussed: IContextKey; + private inputPatternIncludesFocussed: IContextKey; + private inputPatternExclusionsFocussed: IContextKey; private firstMatchFocussed: IContextKey; private fileMatchOrMatchFocussed: IContextKey; private fileMatchFocussed: IContextKey; @@ -129,6 +131,8 @@ export class SearchViewlet extends Viewlet { this.toDispose = []; this.viewletVisible = Constants.SearchViewletVisibleKey.bindTo(contextKeyService); this.inputBoxFocussed = Constants.InputBoxFocussedKey.bindTo(this.contextKeyService); + this.inputPatternIncludesFocussed = Constants.PatternIncludesFocussedKey.bindTo(this.contextKeyService); + this.inputPatternExclusionsFocussed = Constants.PatternExcludesFocussedKey.bindTo(this.contextKeyService); this.firstMatchFocussed = Constants.FirstMatchFocusKey.bindTo(contextKeyService); this.fileMatchOrMatchFocussed = Constants.FileMatchOrMatchFocusKey.bindTo(contextKeyService); this.fileMatchFocussed = Constants.FileFocusKey.bindTo(contextKeyService); @@ -217,7 +221,7 @@ export class SearchViewlet extends Viewlet { }); this.inputPatternIncludes.onSubmit(() => this.onQueryChanged(true, true)); - this.trackInputBox(this.inputPatternIncludes.inputFocusTracker); + this.trackInputBox(this.inputPatternIncludes.inputFocusTracker, this.inputPatternIncludesFocussed); }); //pattern exclusion list @@ -240,7 +244,7 @@ export class SearchViewlet extends Viewlet { }); this.inputPatternExclusions.onSubmit(() => this.onQueryChanged(true, true)); - this.trackInputBox(this.inputPatternExclusions.inputFocusTracker); + this.trackInputBox(this.inputPatternExclusions.inputFocusTracker, this.inputPatternExclusionsFocussed); }); // add hint if we have global exclusion @@ -288,6 +292,14 @@ export class SearchViewlet extends Viewlet { return this.searchWidget; } + public get searchIncludePattern(): PatternInputWidget { + return this.inputPatternIncludes; + } + + public get searchExcludePattern(): PatternInputWidget { + return this.inputPatternExclusions; + } + private createSearchWidget(builder: Builder): void { let contentPattern = this.viewletSettings['query.contentPattern'] || ''; let isRegex = this.viewletSettings['query.regex'] === true; @@ -326,15 +338,21 @@ export class SearchViewlet extends Viewlet { this.trackInputBox(this.searchWidget.replaceInputFocusTracker); } - private trackInputBox(inputFocusTracker: dom.IFocusTracker): void { + private trackInputBox(inputFocusTracker: dom.IFocusTracker, contextKey?: IContextKey): void { this.toUnbind.push(inputFocusTracker.addFocusListener(() => { this.inputBoxFocussed.set(true); + if (contextKey) { + contextKey.set(true); + } })); this.toUnbind.push(inputFocusTracker.addBlurListener(() => { this.inputBoxFocussed.set(this.searchWidget.searchInputHasFocus() || this.searchWidget.replaceInputHasFocus() || this.inputPatternIncludes.inputHasFocus() || this.inputPatternExclusions.inputHasFocus()); + if (contextKey) { + contextKey.set(false); + } })); } diff --git a/src/vs/workbench/parts/search/common/constants.ts b/src/vs/workbench/parts/search/common/constants.ts index 08d01f86b26..77b7da9c75f 100644 --- a/src/vs/workbench/parts/search/common/constants.ts +++ b/src/vs/workbench/parts/search/common/constants.ts @@ -25,6 +25,8 @@ export const SearchViewletVisibleKey = new RawContextKey('searchViewlet export const InputBoxFocussedKey = new RawContextKey('inputBoxFocus', false); export const SearchInputBoxFocussedKey = new RawContextKey('searchInputBoxFocus', false); export const ReplaceInputBoxFocussedKey = new RawContextKey('replaceInputBoxFocus', false); +export const PatternIncludesFocussedKey = new RawContextKey('patternIncludesInputBoxFocus', false); +export const PatternExcludesFocussedKey = new RawContextKey('patternExcludesInputBoxFocus', false); export const ReplaceActiveKey = new RawContextKey('replaceActive', false); export const FirstMatchFocusKey = new RawContextKey('firstMatchFocus', false); From 1cbbd1d3ab4df37bcf17c77eae84199fc9e4ba94 Mon Sep 17 00:00:00 2001 From: Ryan Stringham Date: Mon, 29 May 2017 15:57:07 -0600 Subject: [PATCH 002/136] Save search history when shutting down and load when starting up. --- src/vs/base/common/history.ts | 4 ++++ .../parts/search/browser/patternInputWidget.ts | 8 ++++++++ .../parts/search/browser/searchViewlet.ts | 15 +++++++++++++-- .../parts/search/browser/searchWidget.ts | 7 ++++++- 4 files changed, 31 insertions(+), 3 deletions(-) diff --git a/src/vs/base/common/history.ts b/src/vs/base/common/history.ts index 82099d63c30..21d31651376 100644 --- a/src/vs/base/common/history.ts +++ b/src/vs/base/common/history.ts @@ -17,6 +17,10 @@ export class HistoryNavigator implements INavigator { this._onChange(); } + public getHistory(): T[] { + return this._elements; + } + public add(t: T) { this._history.add(t); this._onChange(); diff --git a/src/vs/workbench/parts/search/browser/patternInputWidget.ts b/src/vs/workbench/parts/search/browser/patternInputWidget.ts index 5c071d58847..9a1cc53eaee 100644 --- a/src/vs/workbench/parts/search/browser/patternInputWidget.ts +++ b/src/vs/workbench/parts/search/browser/patternInputWidget.ts @@ -189,6 +189,14 @@ export class PatternInputWidget extends Widget { return this.pattern.width(); } + public getHistory(): string[] { + return this.history.getHistory(); + } + + public setHistory(history: string[]) { + this.history = new HistoryNavigator(history); + } + public showNextTerm() { let next = this.history.next(); if (next) { diff --git a/src/vs/workbench/parts/search/browser/searchViewlet.ts b/src/vs/workbench/parts/search/browser/searchViewlet.ts index 2e73a0cf36a..899bbcd9f2c 100644 --- a/src/vs/workbench/parts/search/browser/searchViewlet.ts +++ b/src/vs/workbench/parts/search/browser/searchViewlet.ts @@ -180,9 +180,11 @@ export class SearchViewlet extends Viewlet { const filePatterns = this.viewletSettings['query.filePatterns'] || ''; const patternExclusions = this.viewletSettings['query.folderExclusions'] || ''; + const patternExclusionsHistory = this.viewletSettings['query.folderExclusionsHistory'] || []; const exclusionsUsePattern = this.viewletSettings['query.exclusionsUsePattern']; const includesUsePattern = this.viewletSettings['query.includesUsePattern']; const patternIncludes = this.viewletSettings['query.folderIncludes'] || ''; + const patternIncludesHistory = this.viewletSettings['query.folderIncludesHistory'] || []; const queryDetailsExpanded = this.viewletSettings['query.queryDetailsExpanded'] || ''; const useIgnoreFiles = typeof this.viewletSettings['query.useIgnoreFiles'] === 'boolean' ? this.viewletSettings['query.useIgnoreFiles'] : @@ -214,6 +216,7 @@ export class SearchViewlet extends Viewlet { this.inputPatternIncludes.setIsGlobPattern(includesUsePattern); this.inputPatternIncludes.setValue(patternIncludes); + this.inputPatternIncludes.setHistory(patternIncludesHistory); this.inputPatternIncludes .on(FindInput.OPTION_CHANGE, (e) => { @@ -237,6 +240,7 @@ export class SearchViewlet extends Viewlet { this.inputPatternExclusions.setValue(patternExclusions); this.inputPatternExclusions.setUseIgnoreFiles(useIgnoreFiles); this.inputPatternExclusions.setUseExcludeSettings(useExcludeSettings); + this.inputPatternExclusions.setHistory(patternExclusionsHistory); this.inputPatternExclusions .on(FindInput.OPTION_CHANGE, (e) => { @@ -305,12 +309,14 @@ export class SearchViewlet extends Viewlet { let isRegex = this.viewletSettings['query.regex'] === true; let isWholeWords = this.viewletSettings['query.wholeWords'] === true; let isCaseSensitive = this.viewletSettings['query.caseSensitive'] === true; + let searchHistory = this.viewletSettings['query.searchHistory'] || []; this.searchWidget = this.instantiationService.createInstance(SearchWidget, builder, { value: contentPattern, isRegex: isRegex, isCaseSensitive: isCaseSensitive, - isWholeWords: isWholeWords + isWholeWords: isWholeWords, + history: searchHistory }); if (this.storageService.getBoolean(SearchViewlet.SHOW_REPLACE_STORAGE_KEY, StorageScope.WORKSPACE, true)) { @@ -1368,23 +1374,28 @@ export class SearchViewlet extends Viewlet { const isWholeWords = this.searchWidget.searchInput.getWholeWords(); const isCaseSensitive = this.searchWidget.searchInput.getCaseSensitive(); const contentPattern = this.searchWidget.searchInput.getValue(); + const searchHistory = this.searchWidget.getHistory(); const patternExcludes = this.inputPatternExclusions.getValue().trim(); + const patternExcludesHistory = this.inputPatternExclusions.getHistory(); const exclusionsUsePattern = this.inputPatternExclusions.isGlobPattern(); const patternIncludes = this.inputPatternIncludes.getValue().trim(); + const patternIncludesHistory = this.inputPatternIncludes.getHistory(); const includesUsePattern = this.inputPatternIncludes.isGlobPattern(); const useIgnoreFiles = this.inputPatternExclusions.useIgnoreFiles(); // store memento this.viewletSettings['query.contentPattern'] = contentPattern; + this.viewletSettings['query.searchHistory'] = searchHistory; this.viewletSettings['query.regex'] = isRegex; this.viewletSettings['query.wholeWords'] = isWholeWords; this.viewletSettings['query.caseSensitive'] = isCaseSensitive; this.viewletSettings['query.folderExclusions'] = patternExcludes; + this.viewletSettings['query.folderExclusionsHistory'] = patternExcludesHistory; this.viewletSettings['query.exclusionsUsePattern'] = exclusionsUsePattern; this.viewletSettings['query.folderIncludes'] = patternIncludes; + this.viewletSettings['query.folderIncludesHistory'] = patternIncludesHistory; this.viewletSettings['query.includesUsePattern'] = includesUsePattern; this.viewletSettings['query.useIgnoreFiles'] = useIgnoreFiles; - this.viewletSettings['query.contentPattern'] = contentPattern; super.shutdown(); } diff --git a/src/vs/workbench/parts/search/browser/searchWidget.ts b/src/vs/workbench/parts/search/browser/searchWidget.ts index c9853dd70a1..88ca67c62bf 100644 --- a/src/vs/workbench/parts/search/browser/searchWidget.ts +++ b/src/vs/workbench/parts/search/browser/searchWidget.ts @@ -35,6 +35,7 @@ export interface ISearchWidgetOptions { isRegex?: boolean; isCaseSensitive?: boolean; isWholeWords?: boolean; + history?: string[]; } class ReplaceAllAction extends Action { @@ -119,7 +120,7 @@ export class SearchWidget extends Widget { @IKeybindingService private keyBindingService2: IKeybindingService, ) { super(); - this.searchHistory = new HistoryNavigator(); + this.searchHistory = new HistoryNavigator(options.history); this.replaceActive = Constants.ReplaceActiveKey.bindTo(this.keyBindingService); this.searchInputBoxFocussed = Constants.SearchInputBoxFocussedKey.bindTo(this.keyBindingService); this.replaceInputBoxFocussed = Constants.ReplaceInputBoxFocussedKey.bindTo(this.keyBindingService); @@ -170,6 +171,10 @@ export class SearchWidget extends Widget { } } + public getHistory(): string[] { + return this.searchHistory.getHistory(); + } + public showNextSearchTerm() { let next = this.searchHistory.next(); if (next) { From cc583e27bcb40b4a5e3f95395a3b2bdb6ded0365 Mon Sep 17 00:00:00 2001 From: cleidigh Date: Wed, 28 Jun 2017 14:08:15 -0400 Subject: [PATCH 003/136] extension install count formatting --- .../workbench/parts/extensions/browser/extensionsWidgets.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/parts/extensions/browser/extensionsWidgets.ts b/src/vs/workbench/parts/extensions/browser/extensionsWidgets.ts index 33e521318c3..761519044e4 100644 --- a/src/vs/workbench/parts/extensions/browser/extensionsWidgets.ts +++ b/src/vs/workbench/parts/extensions/browser/extensionsWidgets.ts @@ -80,10 +80,13 @@ export class InstallWidget implements IDisposable { installLabel = `${Math.floor(installCount / 1000)}K`; } } + else { + installLabel = installCount.toLocaleString('en'); + } append(this.container, $('span.octicon.octicon-cloud-download')); const count = append(this.container, $('span.count')); - count.textContent = installLabel || String(installCount); + count.textContent = installLabel; } dispose(): void { From 101f1407e38ec58e79c129f39a966fc5e8c7187e Mon Sep 17 00:00:00 2001 From: Matt Fehskens Date: Thu, 4 May 2017 23:24:27 -0400 Subject: [PATCH 004/136] Added an Enabled menu item to extensions Set it to default extensions pane to enabled Added action to command palette as well --- .../extensions/browser/extensionsActions.ts | 24 +++++++++++++++++++ .../extensions.contribution.ts | 5 +++- .../electron-browser/extensionsViewlet.ts | 6 +++++ .../electron-browser/extensionsViews.ts | 19 +++++++++++++-- 4 files changed, 51 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/parts/extensions/browser/extensionsActions.ts b/src/vs/workbench/parts/extensions/browser/extensionsActions.ts index 1b9cc797d1f..fc22516ec9e 100644 --- a/src/vs/workbench/parts/extensions/browser/extensionsActions.ts +++ b/src/vs/workbench/parts/extensions/browser/extensionsActions.ts @@ -892,6 +892,30 @@ export class InstallExtensionsAction extends OpenExtensionsViewletAction { static LABEL = localize('installExtensions', "Install Extensions"); } +export class ShowEnabledExtensionsAction extends Action { + + static ID = 'workbench.extensions.action.showEnabledExtensions'; + static LABEL = localize('showEnabledExtensions', 'Show Enabled Extensions'); + + constructor( + id: string, + label: string, + @IViewletService private viewletService: IViewletService, + @IExtensionsWorkbenchService private extensionsWorkbenchService: IExtensionsWorkbenchService + ) { + super(id, label, 'clear-extensions', true); + } + + run(): TPromise { + return this.viewletService.openViewlet(VIEWLET_ID, true) + .then(viewlet => viewlet as IExtensionsViewlet) + .then(viewlet => { + viewlet.search('@enabled'); + viewlet.focus(); + }); + } +} + export class ShowInstalledExtensionsAction extends Action { static ID = 'workbench.extensions.action.showInstalledExtensions'; diff --git a/src/vs/workbench/parts/extensions/electron-browser/extensions.contribution.ts b/src/vs/workbench/parts/extensions/electron-browser/extensions.contribution.ts index 1c3464e2c12..826cb9e9a3c 100644 --- a/src/vs/workbench/parts/extensions/electron-browser/extensions.contribution.ts +++ b/src/vs/workbench/parts/extensions/electron-browser/extensions.contribution.ts @@ -23,7 +23,7 @@ import { VIEWLET_ID, IExtensionsWorkbenchService } from '../common/extensions'; import { ExtensionsWorkbenchService } from 'vs/workbench/parts/extensions/node/extensionsWorkbenchService'; import { OpenExtensionsViewletAction, InstallExtensionsAction, ShowOutdatedExtensionsAction, ShowRecommendedExtensionsAction, ShowRecommendedKeymapExtensionsAction, ShowWorkspaceRecommendedExtensionsAction, ShowPopularExtensionsAction, - ShowInstalledExtensionsAction, ShowDisabledExtensionsAction, UpdateAllAction, ConfigureWorkspaceRecommendedExtensionsAction, + ShowEnabledExtensionsAction, ShowInstalledExtensionsAction, ShowDisabledExtensionsAction, UpdateAllAction, ConfigureWorkspaceRecommendedExtensionsAction, EnableAllAction, EnableAllWorkpsaceAction, DisableAllAction, DisableAllWorkpsaceAction, CheckForUpdatesAction, ShowLanguageExtensionsAction, EnableAutoUpdateAction, DisableAutoUpdateAction } from 'vs/workbench/parts/extensions/browser/extensionsActions'; import { OpenExtensionsFolderAction, InstallVSIXAction } from 'vs/workbench/parts/extensions/electron-browser/extensionsActions'; @@ -127,6 +127,9 @@ actionRegistry.registerWorkbenchAction(workspaceRecommendationsActionDescriptor, const popularActionDescriptor = new SyncActionDescriptor(ShowPopularExtensionsAction, ShowPopularExtensionsAction.ID, ShowPopularExtensionsAction.LABEL); actionRegistry.registerWorkbenchAction(popularActionDescriptor, 'Extensions: Show Popular Extensions', ExtensionsLabel); +const enabledActionDescriptor = new SyncActionDescriptor(ShowEnabledExtensionsAction, ShowEnabledExtensionsAction.ID, ShowEnabledExtensionsAction.LABEL); +actionRegistry.registerWorkbenchAction(enabledActionDescriptor, 'Extensions: Show Enabled Extensions', ExtensionsLabel); + const installedActionDescriptor = new SyncActionDescriptor(ShowInstalledExtensionsAction, ShowInstalledExtensionsAction.ID, ShowInstalledExtensionsAction.LABEL); actionRegistry.registerWorkbenchAction(installedActionDescriptor, 'Extensions: Show Installed Extensions', ExtensionsLabel); diff --git a/src/vs/workbench/parts/extensions/electron-browser/extensionsViewlet.ts b/src/vs/workbench/parts/extensions/electron-browser/extensionsViewlet.ts index 4f8e99d0488..ef53e728a6b 100644 --- a/src/vs/workbench/parts/extensions/electron-browser/extensionsViewlet.ts +++ b/src/vs/workbench/parts/extensions/electron-browser/extensionsViewlet.ts @@ -27,9 +27,14 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti import { IExtensionService } from 'vs/platform/extensions/common/extensions'; import { IExtensionsWorkbenchService, IExtensionsViewlet, VIEWLET_ID, ExtensionState } from '../common/extensions'; import { +<<<<<<< HEAD ShowInstalledExtensionsAction, ShowRecommendedExtensionsAction, ShowWorkspaceRecommendedExtensionsAction, ShowPopularExtensionsAction, ShowDisabledExtensionsAction, ShowOutdatedExtensionsAction, ClearExtensionsInputAction, ChangeSortAction, UpdateAllAction, CheckForUpdatesAction, DisableAllAction, EnableAllAction, EnableAutoUpdateAction, DisableAutoUpdateAction +======= + ShowRecommendedExtensionsAction, ShowWorkspaceRecommendedExtensionsAction, ShowRecommendedKeymapExtensionsAction, ShowPopularExtensionsAction, ShowEnabledExtensionsAction, ShowInstalledExtensionsAction, ShowDisabledExtensionsAction, + ShowOutdatedExtensionsAction, ClearExtensionsInputAction, ChangeSortAction, UpdateAllAction, CheckForUpdatesAction +>>>>>>> Added an Enabled menu item to extensions } from 'vs/workbench/parts/extensions/browser/extensionsActions'; import { LocalExtensionType, IExtensionManagementService } from 'vs/platform/extensionManagement/common/extensionManagement'; import { InstallVSIXAction } from 'vs/workbench/parts/extensions/electron-browser/extensionsActions'; @@ -265,6 +270,7 @@ export class ExtensionsViewlet extends ComposedViewsViewlet implements IExtensio getSecondaryActions(): IAction[] { if (!this.secondaryActions) { this.secondaryActions = [ + this.instantiationService.createInstance(ShowEnabledExtensionsAction, ShowEnabledExtensionsAction.ID, ShowEnabledExtensionsAction.LABEL), this.instantiationService.createInstance(ShowInstalledExtensionsAction, ShowInstalledExtensionsAction.ID, ShowInstalledExtensionsAction.LABEL), this.instantiationService.createInstance(ShowOutdatedExtensionsAction, ShowOutdatedExtensionsAction.ID, ShowOutdatedExtensionsAction.LABEL), this.instantiationService.createInstance(ShowDisabledExtensionsAction, ShowDisabledExtensionsAction.ID, ShowDisabledExtensionsAction.LABEL), diff --git a/src/vs/workbench/parts/extensions/electron-browser/extensionsViews.ts b/src/vs/workbench/parts/extensions/electron-browser/extensionsViews.ts index 0d0e0315e06..858e07486cd 100644 --- a/src/vs/workbench/parts/extensions/electron-browser/extensionsViews.ts +++ b/src/vs/workbench/parts/extensions/electron-browser/extensionsViews.ts @@ -202,8 +202,8 @@ export class ExtensionsListView extends CollapsibleView { return new PagedModel(result); } - if (/@disabled/i.test(value)) { - value = value.replace(/@disabled/g, '').trim().toLowerCase(); + if (/(@disabled|@enabled:false)/i.test(value)) { + value = value.replace(/(@disabled|@enabled:false)/g, '').trim().toLowerCase(); const local = await this.extensionsWorkbenchService.queryLocal(); const runningExtensions = await this.extensionService.getExtensions(); @@ -215,6 +215,21 @@ export class ExtensionsListView extends CollapsibleView { return new PagedModel(result); } + if (/@enabled(:true)?/i.test(value)) { + value = value ? value.replace(/@enabled(:true)?/g, '').trim().toLowerCase() : ''; + + const local = await this.extensionsWorkbenchService.queryLocal(); + + const result = local + .sort((e1, e2) => e1.displayName.localeCompare(e2.displayName)) + .filter(e => e.type === LocalExtensionType.User && + !(e.disabledForWorkspace || e.disabledGlobally) && + e.name.toLowerCase().indexOf(value) > -1 + ); + + return new PagedModel(result); + } + if (ExtensionsListView.isWorkspaceRecommendedExtensionsQuery(query.value)) { return this.getWorkspaceRecommendationsModel(query, options); } else if (ExtensionsListView.isKeymapsRecommendedExtensionsQuery(query.value)) { From f8d258f5a9415304f788869779f83b80215902f9 Mon Sep 17 00:00:00 2001 From: Bugra Cuhadaroglu Date: Mon, 10 Jul 2017 23:25:42 -0400 Subject: [PATCH 005/136] Fixing merge conflict. --- .../parts/extensions/electron-browser/extensionsViewlet.ts | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/vs/workbench/parts/extensions/electron-browser/extensionsViewlet.ts b/src/vs/workbench/parts/extensions/electron-browser/extensionsViewlet.ts index ef53e728a6b..a5ddc130e2d 100644 --- a/src/vs/workbench/parts/extensions/electron-browser/extensionsViewlet.ts +++ b/src/vs/workbench/parts/extensions/electron-browser/extensionsViewlet.ts @@ -27,14 +27,9 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti import { IExtensionService } from 'vs/platform/extensions/common/extensions'; import { IExtensionsWorkbenchService, IExtensionsViewlet, VIEWLET_ID, ExtensionState } from '../common/extensions'; import { -<<<<<<< HEAD - ShowInstalledExtensionsAction, ShowRecommendedExtensionsAction, ShowWorkspaceRecommendedExtensionsAction, ShowPopularExtensionsAction, ShowDisabledExtensionsAction, + ShowEnabledExtensionsAction, ShowInstalledExtensionsAction, ShowRecommendedExtensionsAction, ShowWorkspaceRecommendedExtensionsAction, ShowPopularExtensionsAction, ShowDisabledExtensionsAction, ShowOutdatedExtensionsAction, ClearExtensionsInputAction, ChangeSortAction, UpdateAllAction, CheckForUpdatesAction, DisableAllAction, EnableAllAction, EnableAutoUpdateAction, DisableAutoUpdateAction -======= - ShowRecommendedExtensionsAction, ShowWorkspaceRecommendedExtensionsAction, ShowRecommendedKeymapExtensionsAction, ShowPopularExtensionsAction, ShowEnabledExtensionsAction, ShowInstalledExtensionsAction, ShowDisabledExtensionsAction, - ShowOutdatedExtensionsAction, ClearExtensionsInputAction, ChangeSortAction, UpdateAllAction, CheckForUpdatesAction ->>>>>>> Added an Enabled menu item to extensions } from 'vs/workbench/parts/extensions/browser/extensionsActions'; import { LocalExtensionType, IExtensionManagementService } from 'vs/platform/extensionManagement/common/extensionManagement'; import { InstallVSIXAction } from 'vs/workbench/parts/extensions/electron-browser/extensionsActions'; From d2f5c92cfce5e22d4c4b6001322341c1b56c9516 Mon Sep 17 00:00:00 2001 From: Amadare42 Date: Sun, 16 Jul 2017 23:45:57 +0300 Subject: [PATCH 006/136] small typing fix --- src/vs/vscode.d.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/vscode.d.ts b/src/vs/vscode.d.ts index 679697af4ce..9fc440f5f22 100644 --- a/src/vs/vscode.d.ts +++ b/src/vs/vscode.d.ts @@ -1495,7 +1495,7 @@ declare module 'vscode' { /** * An optional function that is invoked whenever an item is selected. */ - onDidSelectItem?(item: T | string): any; + onDidSelectItem?(item: QuickPickItem | string): any; } /** From ea3d253c88f81b55396e16ac7829baccbd8fae7d Mon Sep 17 00:00:00 2001 From: cleidigh Date: Sat, 22 Jul 2017 14:00:13 -0400 Subject: [PATCH 007/136] Fix Alt+Click for terminal links --- .../electron-browser/terminalLinkHandler.ts | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/parts/terminal/electron-browser/terminalLinkHandler.ts b/src/vs/workbench/parts/terminal/electron-browser/terminalLinkHandler.ts index 513ff9946d4..9d21f691db4 100644 --- a/src/vs/workbench/parts/terminal/electron-browser/terminalLinkHandler.ts +++ b/src/vs/workbench/parts/terminal/electron-browser/terminalLinkHandler.ts @@ -72,7 +72,12 @@ export class TerminalLinkHandler { const baseLocalLinkClause = _platform === platform.Platform.Windows ? winLocalLinkClause : unixLocalLinkClause; // Append line and column number regex this._localLinkPattern = new RegExp(`${baseLocalLinkClause}(${lineAndColumnClause})`); - this._xterm.setHypertextLinkHandler(this._wrapLinkHandler(() => true)); + + this._xterm.setHypertextLinkHandler(this._wrapLinkHandler(uri => { + this._handleHypertextLink(uri); + return; + })); + this._xterm.setHypertextValidationCallback((uri: string, element: HTMLElement, callback: (isValid: boolean) => void) => { this._validateWebLink(uri, element, callback); }); @@ -117,9 +122,10 @@ export class TerminalLinkHandler { private _wrapLinkHandler(handler: (uri: string) => boolean | void): XtermLinkMatcherHandler { return (event: MouseEvent, uri: string) => { + // Prevent default electron link handling so Alt+Click mode works normally + event.preventDefault(); // Require correct modifier on click if (!this._isLinkActivationModifierDown(event)) { - event.preventDefault(); return false; } return handler(uri); @@ -164,6 +170,12 @@ export class TerminalLinkHandler { callback(true); } + private _handleHypertextLink(url: string) { + let uri = Uri.parse(url); + this._openerService.open(uri); + return; + } + private _isLinkActivationModifierDown(event: MouseEvent): boolean { const editorConf = this._configurationService.getConfiguration<{ multiCursorModifier: 'ctrlCmd' | 'alt' }>('editor'); if (editorConf.multiCursorModifier === 'ctrlCmd') { From b2101ca1e5975f9dc3bd09ea7a94107b5b9bb5eb Mon Sep 17 00:00:00 2001 From: cleidigh Date: Sun, 23 Jul 2017 13:00:02 -0400 Subject: [PATCH 008/136] Fix Alt+Click for terminal links - Cleanup --- .../parts/terminal/electron-browser/terminalLinkHandler.ts | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/vs/workbench/parts/terminal/electron-browser/terminalLinkHandler.ts b/src/vs/workbench/parts/terminal/electron-browser/terminalLinkHandler.ts index 9d21f691db4..d58052ff138 100644 --- a/src/vs/workbench/parts/terminal/electron-browser/terminalLinkHandler.ts +++ b/src/vs/workbench/parts/terminal/electron-browser/terminalLinkHandler.ts @@ -75,7 +75,6 @@ export class TerminalLinkHandler { this._xterm.setHypertextLinkHandler(this._wrapLinkHandler(uri => { this._handleHypertextLink(uri); - return; })); this._xterm.setHypertextValidationCallback((uri: string, element: HTMLElement, callback: (isValid: boolean) => void) => { @@ -106,7 +105,6 @@ export class TerminalLinkHandler { public registerLocalLinkHandler(): number { const wrappedHandler = this._wrapLinkHandler(url => { this._handleLocalLink(url); - return; }); return this._xterm.registerLinkMatcher(this._localLinkRegex, wrappedHandler, { @@ -170,10 +168,9 @@ export class TerminalLinkHandler { callback(true); } - private _handleHypertextLink(url: string) { + private _handleHypertextLink(url: string): void { let uri = Uri.parse(url); this._openerService.open(uri); - return; } private _isLinkActivationModifierDown(event: MouseEvent): boolean { From b6d7f1cac009a7b33b541df29f1c0db263eea8a9 Mon Sep 17 00:00:00 2001 From: Andre Weinand Date: Mon, 24 Jul 2017 11:36:45 +0200 Subject: [PATCH 009/136] node-debug@1.15.14 --- build/gulpfile.vscode.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/gulpfile.vscode.js b/build/gulpfile.vscode.js index f717ca4d6bf..61b16090fa2 100644 --- a/build/gulpfile.vscode.js +++ b/build/gulpfile.vscode.js @@ -43,7 +43,7 @@ const nodeModules = ['electron', 'original-fs'] // Build const builtInExtensions = [ - { name: 'ms-vscode.node-debug', version: '1.15.13' }, + { name: 'ms-vscode.node-debug', version: '1.15.14' }, { name: 'ms-vscode.node-debug2', version: '1.14.5' } ]; From 4e6ee69aff75ddbab759691adceb96ac34ab3cf6 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Mon, 24 Jul 2017 19:35:10 +0530 Subject: [PATCH 010/136] Implement #29831 --- src/vs/editor/common/config/commonEditorConfig.ts | 2 +- .../configuration/common/configurationRegistry.ts | 6 +++--- .../parts/files/browser/files.contribution.ts | 12 ++++++------ .../parts/search/browser/search.contribution.ts | 2 +- .../configuration/common/configurationModels.ts | 8 ++++---- .../services/configuration/node/configuration.ts | 11 +++++++++-- .../test/common/configurationModels.test.ts | 10 +++++----- 7 files changed, 29 insertions(+), 22 deletions(-) diff --git a/src/vs/editor/common/config/commonEditorConfig.ts b/src/vs/editor/common/config/commonEditorConfig.ts index 65a75a62ebe..c0666b78a98 100644 --- a/src/vs/editor/common/config/commonEditorConfig.ts +++ b/src/vs/editor/common/config/commonEditorConfig.ts @@ -173,7 +173,7 @@ const editorConfiguration: IConfigurationNode = { 'type': 'object', 'title': nls.localize('editorConfigurationTitle', "Editor"), 'overridable': true, - 'scope': ConfigurationScope.FOLDER, + 'scope': ConfigurationScope.RESOURCE, 'properties': { 'editor.fontFamily': { 'type': 'string', diff --git a/src/vs/platform/configuration/common/configurationRegistry.ts b/src/vs/platform/configuration/common/configurationRegistry.ts index 356f78076ae..e3a6e8d5778 100644 --- a/src/vs/platform/configuration/common/configurationRegistry.ts +++ b/src/vs/platform/configuration/common/configurationRegistry.ts @@ -53,8 +53,8 @@ export interface IConfigurationRegistry { } export enum ConfigurationScope { - WORKSPACE = 1, - FOLDER + WORKBENCH = 1, + RESOURCE } export interface IConfigurationPropertySchema extends IJSONSchema { @@ -154,7 +154,7 @@ class ConfigurationRegistry implements IConfigurationRegistry { } } - private validateAndRegisterProperties(configuration: IConfigurationNode, validate: boolean = true, scope: ConfigurationScope = ConfigurationScope.WORKSPACE, overridable: boolean = false) { + private validateAndRegisterProperties(configuration: IConfigurationNode, validate: boolean = true, scope: ConfigurationScope = ConfigurationScope.WORKBENCH, overridable: boolean = false) { scope = configuration.scope !== void 0 && configuration.scope !== null ? configuration.scope : scope; overridable = configuration.overridable || overridable; let properties = configuration.properties; diff --git a/src/vs/workbench/parts/files/browser/files.contribution.ts b/src/vs/workbench/parts/files/browser/files.contribution.ts index 9f1388cfa35..3f9cf071693 100644 --- a/src/vs/workbench/parts/files/browser/files.contribution.ts +++ b/src/vs/workbench/parts/files/browser/files.contribution.ts @@ -174,7 +174,7 @@ configurationRegistry.registerConfiguration({ 'type': 'object', 'description': nls.localize('exclude', "Configure glob patterns for excluding files and folders."), 'default': { '**/.git': true, '**/.svn': true, '**/.hg': true, '**/CVS': true, '**/.DS_Store': true }, - 'scope': ConfigurationScope.FOLDER, + 'scope': ConfigurationScope.RESOURCE, 'additionalProperties': { 'anyOf': [ { @@ -204,13 +204,13 @@ configurationRegistry.registerConfiguration({ 'enum': Object.keys(SUPPORTED_ENCODINGS), 'default': 'utf8', 'description': nls.localize('encoding', "The default character set encoding to use when reading and writing files."), - 'scope': ConfigurationScope.FOLDER + 'scope': ConfigurationScope.RESOURCE }, 'files.autoGuessEncoding': { 'type': 'boolean', 'default': false, 'description': nls.localize('autoGuessEncoding', "When enabled, will attempt to guess the character set encoding when opening files"), - 'scope': ConfigurationScope.FOLDER + 'scope': ConfigurationScope.RESOURCE }, 'files.eol': { 'type': 'string', @@ -226,14 +226,14 @@ configurationRegistry.registerConfiguration({ 'default': false, 'description': nls.localize('trimTrailingWhitespace', "When enabled, will trim trailing whitespace when saving a file."), 'overridable': true, - 'scope': ConfigurationScope.FOLDER + 'scope': ConfigurationScope.RESOURCE }, 'files.insertFinalNewline': { 'type': 'boolean', 'default': false, 'description': nls.localize('insertFinalNewline', "When enabled, insert a final new line at the end of the file when saving it."), 'overridable': true, - 'scope': ConfigurationScope.FOLDER + 'scope': ConfigurationScope.RESOURCE }, 'files.autoSave': { 'type': 'string', @@ -256,7 +256,7 @@ configurationRegistry.registerConfiguration({ 'type': 'object', 'default': platform.isWindows /* https://github.com/Microsoft/vscode/issues/23954 */ ? { '**/.git/objects/**': true, '**/.git/subtree-cache/**': true, '**/node_modules/*/**': true } : { '**/.git/objects/**': true, '**/.git/subtree-cache/**': true, '**/node_modules/**': true }, 'description': nls.localize('watcherExclude', "Configure glob patterns of file paths to exclude from file watching. Patterns must match on absolute paths (i.e. prefix with ** or the full path to match properly). Changing this setting requires a restart. When you experience Code consuming lots of cpu time on startup, you can exclude large folders to reduce the initial load."), - 'scope': ConfigurationScope.FOLDER + 'scope': ConfigurationScope.RESOURCE }, 'files.hotExit': { 'type': 'string', diff --git a/src/vs/workbench/parts/search/browser/search.contribution.ts b/src/vs/workbench/parts/search/browser/search.contribution.ts index 948143d11b0..adaa1d8591d 100644 --- a/src/vs/workbench/parts/search/browser/search.contribution.ts +++ b/src/vs/workbench/parts/search/browser/search.contribution.ts @@ -363,7 +363,7 @@ configurationRegistry.registerConfiguration({ } ] }, - 'scope': ConfigurationScope.FOLDER + 'scope': ConfigurationScope.RESOURCE }, 'search.useRipgrep': { 'type': 'boolean', diff --git a/src/vs/workbench/services/configuration/common/configurationModels.ts b/src/vs/workbench/services/configuration/common/configurationModels.ts index ab1d0a40140..8acb1f796cb 100644 --- a/src/vs/workbench/services/configuration/common/configurationModels.ts +++ b/src/vs/workbench/services/configuration/common/configurationModels.ts @@ -129,11 +129,11 @@ export class FolderSettingsModel extends CustomConfigurationModel { } public createWorkspaceConfigurationModel(): ConfigurationModel { - return this.createScopedConfigurationModel(ConfigurationScope.WORKSPACE); + return this.createScopedConfigurationModel(ConfigurationScope.WORKBENCH); } public createFolderScopedConfigurationModel(): ConfigurationModel { - return this.createScopedConfigurationModel(ConfigurationScope.FOLDER); + return this.createScopedConfigurationModel(ConfigurationScope.RESOURCE); } private createScopedConfigurationModel(scope: ConfigurationScope): ConfigurationModel { @@ -151,7 +151,7 @@ export class FolderSettingsModel extends CustomConfigurationModel { private getScope(key: string, configurationProperties: { [qualifiedKey: string]: IConfigurationPropertySchema }): ConfigurationScope { const propertySchema = configurationProperties[key]; - return propertySchema ? propertySchema.scope : ConfigurationScope.WORKSPACE; + return propertySchema ? propertySchema.scope : ConfigurationScope.WORKBENCH; } } @@ -166,7 +166,7 @@ export class FolderConfigurationModel extends CustomConfigurationModel { this._contents = {}; this._overrides = []; - this.doMerge(this, ConfigurationScope.WORKSPACE === this.scope ? this.workspaceSettingsConfig : this.workspaceSettingsConfig.createFolderScopedConfigurationModel()); + this.doMerge(this, ConfigurationScope.WORKBENCH === this.scope ? this.workspaceSettingsConfig : this.workspaceSettingsConfig.createFolderScopedConfigurationModel()); for (const configModel of this.scopedConfigs) { this.doMerge(this, configModel); } diff --git a/src/vs/workbench/services/configuration/node/configuration.ts b/src/vs/workbench/services/configuration/node/configuration.ts index 29f2d247f14..4c506d62a25 100644 --- a/src/vs/workbench/services/configuration/node/configuration.ts +++ b/src/vs/workbench/services/configuration/node/configuration.ts @@ -76,6 +76,11 @@ const configurationExtPoint = ExtensionsRegistry.registerExtensionPoint(new FolderSettingsModel(null), [], ConfigurationScope.FOLDER), false); + this.cachedFolderConfigs.set(folder, this._register(new FolderConfiguration(folder, this.workspaceSettingsRootFolder, this.hasMultiFolderWorkspace() ? ConfigurationScope.RESOURCE : ConfigurationScope.WORKBENCH))); + this.updateFolderConfiguration(folder, new FolderConfigurationModel(new FolderSettingsModel(null), [], ConfigurationScope.RESOURCE), false); } } diff --git a/src/vs/workbench/services/configuration/test/common/configurationModels.test.ts b/src/vs/workbench/services/configuration/test/common/configurationModels.test.ts index ed5902bacb1..321bc7efc06 100644 --- a/src/vs/workbench/services/configuration/test/common/configurationModels.test.ts +++ b/src/vs/workbench/services/configuration/test/common/configurationModels.test.ts @@ -15,7 +15,7 @@ suite('ConfigurationService - Model', () => { awesome: true })); - const testObject = new FolderConfigurationModel(settingsConfig, [], ConfigurationScope.WORKSPACE); + const testObject = new FolderConfigurationModel(settingsConfig, [], ConfigurationScope.WORKBENCH); assert.equal(testObject.getContentsFor('task'), undefined); }); @@ -36,7 +36,7 @@ suite('ConfigurationService - Model', () => { } }; - assert.deepEqual(new FolderConfigurationModel(settingsConfig, [tasksConfig], ConfigurationScope.WORKSPACE).contents, expected); + assert.deepEqual(new FolderConfigurationModel(settingsConfig, [tasksConfig], ConfigurationScope.WORKBENCH).contents, expected); }); test('Test consolidate (settings and launch)', () => { @@ -55,7 +55,7 @@ suite('ConfigurationService - Model', () => { } }; - assert.deepEqual(new FolderConfigurationModel(settingsConfig, [launchConfig], ConfigurationScope.WORKSPACE).contents, expected); + assert.deepEqual(new FolderConfigurationModel(settingsConfig, [launchConfig], ConfigurationScope.WORKBENCH).contents, expected); }); test('Test consolidate (settings and launch and tasks) - launch/tasks wins over settings file', () => { @@ -91,7 +91,7 @@ suite('ConfigurationService - Model', () => { } }; - assert.deepEqual(new FolderConfigurationModel(settingsConfig, [launchConfig, tasksConfig], ConfigurationScope.WORKSPACE).contents, expected); - assert.deepEqual(new FolderConfigurationModel(settingsConfig, [tasksConfig, launchConfig], ConfigurationScope.WORKSPACE).contents, expected); + assert.deepEqual(new FolderConfigurationModel(settingsConfig, [launchConfig, tasksConfig], ConfigurationScope.WORKBENCH).contents, expected); + assert.deepEqual(new FolderConfigurationModel(settingsConfig, [tasksConfig, launchConfig], ConfigurationScope.WORKBENCH).contents, expected); }); }); \ No newline at end of file From adf73b89f2aaadd0e2eac39034070b059aeaa0a0 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Mon, 24 Jul 2017 19:45:02 +0530 Subject: [PATCH 011/136] #29462 IConfigurationService.keys - Provide folder specific keys --- src/vs/platform/configuration/common/configuration.ts | 9 ++++++--- .../platform/configuration/node/configurationService.ts | 8 ++++---- .../test/common/testConfigurationService.ts | 3 ++- .../test/electron-browser/telemetryService.test.ts | 2 +- .../test/electron-browser/terminalConfigHelper.test.ts | 2 +- .../services/configuration/node/configuration.ts | 4 ++-- .../test/node/configurationResolverService.test.ts | 2 +- 7 files changed, 17 insertions(+), 13 deletions(-) diff --git a/src/vs/platform/configuration/common/configuration.ts b/src/vs/platform/configuration/common/configuration.ts index 5685e78cd41..85e3e00ff4c 100644 --- a/src/vs/platform/configuration/common/configuration.ts +++ b/src/vs/platform/configuration/common/configuration.ts @@ -43,7 +43,7 @@ export interface IConfigurationService { * Returns the defined keys of configurations in the different scopes * the key is defined. */ - keys(): IConfigurationKeys; + keys(overrides?: IConfigurationOverrides): IConfigurationKeys; /** * Similar to #getConfiguration() but ensures that the latest configuration @@ -91,6 +91,7 @@ export interface IConfigurationKeys { default: string[]; user: string[]; workspace: string[]; + folder: string[]; } /** @@ -268,11 +269,13 @@ export class Configuration { }; } - keys(): IConfigurationKeys { + keys(overrides: IConfigurationOverrides = {}): IConfigurationKeys { + const folderConfigurationModel = this.getFolderConfigurationModelForResource(overrides.resource); return { default: this._defaults.keys, user: this._user.keys, - workspace: this._workspaceConfiguration.keys + workspace: this._workspaceConfiguration.keys, + folder: folderConfigurationModel ? folderConfigurationModel.keys : [] }; } diff --git a/src/vs/platform/configuration/node/configurationService.ts b/src/vs/platform/configuration/node/configurationService.ts index 5ce78912ee2..afe3e8d1cf7 100644 --- a/src/vs/platform/configuration/node/configurationService.ts +++ b/src/vs/platform/configuration/node/configurationService.ts @@ -72,12 +72,12 @@ export class ConfigurationService extends Disposable implements IConfiguratio return this.configuration().getValue(section, options); } - public lookup(key: string, options?: IConfigurationOverrides): IConfigurationValue { - return this.configuration().lookup(key, options); + public lookup(key: string, overrides?: IConfigurationOverrides): IConfigurationValue { + return this.configuration().lookup(key, overrides); } - public keys(): IConfigurationKeys { - return this.configuration().keys(); + public keys(overrides?: IConfigurationOverrides): IConfigurationKeys { + return this.configuration().keys(overrides); } public values(): IConfigurationValues { diff --git a/src/vs/platform/configuration/test/common/testConfigurationService.ts b/src/vs/platform/configuration/test/common/testConfigurationService.ts index 2f7161f56df..a49f1aff2dd 100644 --- a/src/vs/platform/configuration/test/common/testConfigurationService.ts +++ b/src/vs/platform/configuration/test/common/testConfigurationService.ts @@ -50,7 +50,8 @@ export class TestConfigurationService extends EventEmitter implements IConfigura return { default: getConfigurationKeys(), user: Object.keys(this.configuration), - workspace: [] + workspace: [], + folder: [] }; } diff --git a/src/vs/platform/telemetry/test/electron-browser/telemetryService.test.ts b/src/vs/platform/telemetry/test/electron-browser/telemetryService.test.ts index 6acd2f7754a..494159a5f56 100644 --- a/src/vs/platform/telemetry/test/electron-browser/telemetryService.test.ts +++ b/src/vs/platform/telemetry/test/electron-browser/telemetryService.test.ts @@ -696,7 +696,7 @@ suite('TelemetryService', () => { folder: null }; }, - keys() { return { default: [], user: [], workspace: [] }; }, + keys() { return { default: [], user: [], workspace: [], folder: [] }; }, values() { return {}; }, onDidUpdateConfiguration: emitter.event }); diff --git a/src/vs/workbench/parts/terminal/test/electron-browser/terminalConfigHelper.test.ts b/src/vs/workbench/parts/terminal/test/electron-browser/terminalConfigHelper.test.ts index c4b3e37919a..b370e03061b 100644 --- a/src/vs/workbench/parts/terminal/test/electron-browser/terminalConfigHelper.test.ts +++ b/src/vs/workbench/parts/terminal/test/electron-browser/terminalConfigHelper.test.ts @@ -19,7 +19,7 @@ class MockConfigurationService implements IConfigurationService { public constructor(private configuration: any = {}) { } public reloadConfiguration(section?: string): TPromise { return TPromise.as(this.getConfiguration()); } public lookup(key: string, overrides?: IConfigurationOverrides): IConfigurationValue { return { value: getConfigurationValue(this.getConfiguration(), key), default: getConfigurationValue(this.getConfiguration(), key), user: getConfigurationValue(this.getConfiguration(), key), workspace: void 0, folder: void 0 }; } - public keys() { return { default: [], user: [], workspace: [] }; } + public keys() { return { default: [], user: [], workspace: [], folder: [] }; } public values() { return {}; } public getConfiguration(): any { return this.configuration; } public getConfigurationData(): any { return null; } diff --git a/src/vs/workbench/services/configuration/node/configuration.ts b/src/vs/workbench/services/configuration/node/configuration.ts index 4c506d62a25..2adfbfb452b 100644 --- a/src/vs/workbench/services/configuration/node/configuration.ts +++ b/src/vs/workbench/services/configuration/node/configuration.ts @@ -256,8 +256,8 @@ export class WorkspaceService extends Disposable implements IWorkspaceConfigurat return this._configuration.lookup(key, overrides); } - public keys(): IConfigurationKeys { - return this._configuration.keys(); + public keys(overrides?: IConfigurationOverrides): IConfigurationKeys { + return this._configuration.keys(overrides); } public values(): IConfigurationValues { diff --git a/src/vs/workbench/services/configurationResolver/test/node/configurationResolverService.test.ts b/src/vs/workbench/services/configurationResolver/test/node/configurationResolverService.test.ts index 4ade2612bd4..f0b67873917 100644 --- a/src/vs/workbench/services/configurationResolver/test/node/configurationResolverService.test.ts +++ b/src/vs/workbench/services/configurationResolver/test/node/configurationResolverService.test.ts @@ -343,7 +343,7 @@ class MockConfigurationService implements IConfigurationService { public constructor(private configuration: any = {}) { } public reloadConfiguration(section?: string): TPromise { return TPromise.as(this.getConfiguration()); } public lookup(key: string, overrides?: IConfigurationOverrides): IConfigurationValue { return { value: getConfigurationValue(this.getConfiguration(), key), default: getConfigurationValue(this.getConfiguration(), key), user: getConfigurationValue(this.getConfiguration(), key), workspace: void 0, folder: void 0 }; } - public keys() { return { default: [], user: [], workspace: [] }; } + public keys() { return { default: [], user: [], workspace: [], folder: [] }; } public values() { return {}; } public getConfiguration(): any { return this.configuration; } public getConfigurationData(): any { return null; } From 2bac5fd09f83726986f8745b3d9c7e68fa1531a9 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Mon, 24 Jul 2017 23:05:21 +0530 Subject: [PATCH 012/136] #29462 Indicate unsupported settings on a folder level --- .../preferences/browser/media/preferences.css | 4 + .../preferences/browser/preferencesActions.ts | 5 +- .../preferences/browser/preferencesEditor.ts | 29 ++++--- .../browser/preferencesRenderers.ts | 85 ++++++++++++++++++- .../preferences/browser/preferencesService.ts | 70 ++++++++------- .../preferences/browser/preferencesWidgets.ts | 42 ++++----- .../parts/preferences/common/preferences.ts | 13 ++- .../common/configurationEditing.ts | 12 ++- .../node/configurationEditingService.ts | 52 +++++++++--- 9 files changed, 218 insertions(+), 94 deletions(-) diff --git a/src/vs/workbench/parts/preferences/browser/media/preferences.css b/src/vs/workbench/parts/preferences/browser/media/preferences.css index 6d074583c7f..eee39c73482 100644 --- a/src/vs/workbench/parts/preferences/browser/media/preferences.css +++ b/src/vs/workbench/parts/preferences/browser/media/preferences.css @@ -213,6 +213,10 @@ background: url('edit_inverse.svg') center center no-repeat; } +.monaco-editor .invalidSetting { + color: #b1b1b1; +} + .monaco-editor .floating-click-widget { padding: 10px; border-radius: 5px; diff --git a/src/vs/workbench/parts/preferences/browser/preferencesActions.ts b/src/vs/workbench/parts/preferences/browser/preferencesActions.ts index a528ab2d951..ee92a54842f 100644 --- a/src/vs/workbench/parts/preferences/browser/preferencesActions.ts +++ b/src/vs/workbench/parts/preferences/browser/preferencesActions.ts @@ -12,6 +12,7 @@ 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"; export class OpenGlobalSettingsAction extends Action { @@ -106,7 +107,7 @@ export class OpenFolderSettingsAction extends Action { public run(): TPromise { const picks: IPickOpenEntry[] = this.workspaceContextService.getWorkspace().roots.map((root, index) => { return { - label: getSettingsTargetName(root, this.workspaceContextService), + label: getSettingsTargetName(ConfigurationTarget.FOLDER, root, this.workspaceContextService), id: `${index}` }; }); @@ -114,7 +115,7 @@ export class OpenFolderSettingsAction extends Action { return this.quickOpenService.pick(picks, { placeHolder: nls.localize('pickFolder', "Select Folder") }) .then(pick => { if (pick) { - return this.preferencesService.openSettings(this.workspaceContextService.getWorkspace().roots[parseInt(pick.id)]); + return this.preferencesService.openFolderSettings(this.workspaceContextService.getWorkspace().roots[parseInt(pick.id)]); } return undefined; }); diff --git a/src/vs/workbench/parts/preferences/browser/preferencesEditor.ts b/src/vs/workbench/parts/preferences/browser/preferencesEditor.ts index 95644da856d..340b196705b 100644 --- a/src/vs/workbench/parts/preferences/browser/preferencesEditor.ts +++ b/src/vs/workbench/parts/preferences/browser/preferencesEditor.ts @@ -43,7 +43,7 @@ import { ConfigurationTarget } from 'vs/workbench/services/configuration/common/ import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { VSash } from 'vs/base/browser/ui/sash/sash'; import { Widget } from 'vs/base/browser/ui/widget'; -import { IPreferencesRenderer, DefaultSettingsRenderer, UserSettingsRenderer, WorkspaceSettingsRenderer } from 'vs/workbench/parts/preferences/browser/preferencesRenderers'; +import { IPreferencesRenderer, DefaultSettingsRenderer, UserSettingsRenderer, WorkspaceSettingsRenderer, FolderSettingsRenderer } from 'vs/workbench/parts/preferences/browser/preferencesRenderers'; import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; import { IEditorGroupService } from 'vs/workbench/services/group/common/groupService'; import { getCodeEditor } from 'vs/editor/common/services/codeEditorService'; @@ -147,7 +147,7 @@ export class PreferencesEditor extends BaseEditor { this._register(this.searchWidget.onFocus(() => this.lastFocusedWidget = this.searchWidget)); this.lastFocusedWidget = this.searchWidget; - this.settingsTargetsWidget = this._register(this.instantiationService.createInstance(SettingsTargetsWidget, this.headerContainer, this.preferencesService.userSettingsResource)); + this.settingsTargetsWidget = this._register(this.instantiationService.createInstance(SettingsTargetsWidget, this.headerContainer, this.preferencesService.userSettingsResource, ConfigurationTarget.USER)); this._register(this.settingsTargetsWidget.onDidTargetChange(target => this.switchSettings(target))); const editorsContainer = DOM.append(parentElement, DOM.$('.preferences-editors-container')); @@ -211,7 +211,8 @@ export class PreferencesEditor extends BaseEditor { } private updateInput(oldInput: PreferencesEditorInput, newInput: PreferencesEditorInput, options?: EditorOptions): TPromise { - this.settingsTargetsWidget.setTarget(this.getSettingsConfigurationTarget(newInput)); + const resource = toResource(newInput.master); + this.settingsTargetsWidget.setTarget(resource, this.getSettingsConfigurationTarget(resource)); return this.sideBySidePreferencesWidget.setInput(newInput.details, newInput.master, options).then(({ defaultPreferencesRenderer, editablePreferencesRenderer }) => { this.preferencesRenderers.defaultPreferencesRenderer = defaultPreferencesRenderer; @@ -220,24 +221,26 @@ export class PreferencesEditor extends BaseEditor { }); } - private getSettingsConfigurationTarget(preferencesEditorInput: PreferencesEditorInput): ConfigurationTarget | URI { - const resource = toResource(preferencesEditorInput.master); + private getSettingsConfigurationTarget(resource: URI): ConfigurationTarget { if (this.preferencesService.userSettingsResource.fsPath === resource.fsPath) { return ConfigurationTarget.USER; } if (this.preferencesService.workspaceSettingsResource.fsPath === resource.fsPath) { return ConfigurationTarget.WORKSPACE; } - return this.workspaceContextService.getRoot(resource); + if (this.workspaceContextService.getRoot(resource)) { + return ConfigurationTarget.FOLDER; + } + return null; } - private switchSettings(target: ConfigurationTarget | URI): void { + private switchSettings(resource: URI): void { // Focus the editor if this editor is not active editor if (this.editorService.getActiveEditor() !== this) { this.focus(); } const promise = this.input.isDirty() ? this.input.save() : TPromise.as(true); - promise.done(value => this.preferencesService.switchSettings(target)); + promise.done(value => this.preferencesService.switchSettings(this.getSettingsConfigurationTarget(resource), resource)); } private filterPreferences(filter: string) { @@ -811,10 +814,14 @@ class SettingsEditorContribution extends Disposable implements ISettingsEditorCo this.preferencesRenderer = TPromise.join([this.preferencesService.createPreferencesEditorModel(this.preferencesService.defaultSettingsResource), this.preferencesService.createPreferencesEditorModel(this.editor.getModel().uri)]) .then(([defaultSettingsModel, settingsModel]) => { if (settingsModel instanceof SettingsEditorModel) { - if (ConfigurationTarget.USER === settingsModel.configurationTarget) { - return this.instantiationService.createInstance(UserSettingsRenderer, this.editor, settingsModel, defaultSettingsModel); + switch (settingsModel.configurationTarget) { + case ConfigurationTarget.USER: + return this.instantiationService.createInstance(UserSettingsRenderer, this.editor, settingsModel, defaultSettingsModel); + 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, defaultSettingsModel); } return null; }) diff --git a/src/vs/workbench/parts/preferences/browser/preferencesRenderers.ts b/src/vs/workbench/parts/preferences/browser/preferencesRenderers.ts index 76e6f1d4cc5..0756292a4a2 100644 --- a/src/vs/workbench/parts/preferences/browser/preferencesRenderers.ts +++ b/src/vs/workbench/parts/preferences/browser/preferencesRenderers.ts @@ -13,7 +13,7 @@ import Event, { Emitter } from 'vs/base/common/event'; import { Registry } from 'vs/platform/registry/common/platform'; import * as editorCommon from 'vs/editor/common/editorCommon'; import { Range, IRange } from 'vs/editor/common/core/range'; -import { IConfigurationRegistry, Extensions as ConfigurationExtensions } from 'vs/platform/configuration/common/configurationRegistry'; +import { IConfigurationRegistry, Extensions as ConfigurationExtensions, ConfigurationScope } 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'; @@ -94,7 +94,8 @@ export class UserSettingsRenderer extends Disposable implements IPreferencesRend public updatePreference(key: string, value: any, source: ISetting): void { this.telemetryService.publicLog('defaultSettingsActions.copySetting', { userConfigurationKeys: [key] }); const overrideIdentifier = source.overrideOf ? overrideIdentifierFromKey(source.overrideOf.key) : null; - this.configurationEditingService.writeConfiguration(this.preferencesModel.configurationTarget, { key, value }, { donotSave: this.textFileService.isDirty(this.preferencesModel.uri), donotNotifyError: true, scopes: { overrideIdentifier } }) + 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)); }); @@ -183,6 +184,28 @@ export class WorkspaceSettingsRenderer extends UserSettingsRenderer implements I } } +export class FolderSettingsRenderer extends UserSettingsRenderer implements IPreferencesRenderer { + + private unsupportedWorkbenchSettingsRenderer: UnsupportedWorkbenchSettingsRenderer; + + constructor(editor: ICodeEditor, preferencesModel: SettingsEditorModel, associatedPreferencesModel: IPreferencesEditorModel, + @IPreferencesService preferencesService: IPreferencesService, + @ITelemetryService telemetryService: ITelemetryService, + @ITextFileService textFileService: ITextFileService, + @IConfigurationEditingService configurationEditingService: IConfigurationEditingService, + @IMessageService messageService: IMessageService, + @IInstantiationService instantiationService: IInstantiationService + ) { + super(editor, preferencesModel, associatedPreferencesModel, preferencesService, telemetryService, textFileService, configurationEditingService, messageService, instantiationService); + this.unsupportedWorkbenchSettingsRenderer = this._register(instantiationService.createInstance(UnsupportedWorkbenchSettingsRenderer, editor, preferencesModel)); + } + + public render(): void { + super.render(); + this.unsupportedWorkbenchSettingsRenderer.render(); + } +} + export class DefaultSettingsRenderer extends Disposable implements IPreferencesRenderer { private _associatedPreferencesModel: IPreferencesEditorModel; @@ -929,4 +952,62 @@ class UnsupportedWorkspaceSettingsRenderer extends Disposable { this.markerService.remove('preferencesEditor', [this.workspaceSettingsEditorModel.uri]); super.dispose(); } +} + +class UnsupportedWorkbenchSettingsRenderer extends Disposable { + + private decorationIds: string[] = []; + + constructor(private editor: editorCommon.ICommonCodeEditor, private workspaceSettingsEditorModel: SettingsEditorModel, + @IWorkspaceConfigurationService private configurationService: IWorkspaceConfigurationService, + @IMarkerService private markerService: IMarkerService + ) { + super(); + this._register(this.configurationService.onDidUpdateConfiguration(() => this.render())); + } + + public render(): void { + this.editor.changeDecorations(changeAccessor => this.decorationIds = changeAccessor.deltaDecorations(this.decorationIds, [])); + + const configurationRegistry = Registry.as(ConfigurationExtensions.Configuration).getConfigurationProperties(); + const folderKeys = this.configurationService.keys({ resource: this.workspaceSettingsEditorModel.uri }).folder; + const workbenchKeys = folderKeys.filter(key => configurationRegistry[key] && configurationRegistry[key].scope === ConfigurationScope.WORKBENCH); + if (workbenchKeys.length) { + const ranges: IRange[] = []; + for (const unsupportedKey of workbenchKeys) { + const setting = this.workspaceSettingsEditorModel.getPreference(unsupportedKey); + if (setting) { + ranges.push({ + startLineNumber: setting.keyRange.startLineNumber, + startColumn: setting.keyRange.startColumn - 1, + endLineNumber: setting.valueRange.endLineNumber, + endColumn: setting.valueRange.endColumn + }); + } + } + this.editor.changeDecorations(changeAccessor => this.decorationIds = changeAccessor.deltaDecorations(this.decorationIds, ranges.map(range => this.createDecoration(range, this.editor.getModel())))); + } + } + + private static _INVALID_SETTING_ = ModelDecorationOptions.register({ + stickiness: editorCommon.TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges, + inlineClassName: 'invalidSetting', + hoverMessage: nls.localize('unsupportedWorkbenchSetting', "This setting is a workbench setting and cannot be applied for resources under folder") + }); + + private createDecoration(range: IRange, model: editorCommon.IModel): editorCommon.IModelDeltaDecoration { + return { + range, + options: UnsupportedWorkbenchSettingsRenderer._INVALID_SETTING_ + }; + } + + public dispose(): void { + if (this.decorationIds) { + this.decorationIds = this.editor.changeDecorations(changeAccessor => { + return changeAccessor.deltaDecorations(this.decorationIds, []); + }); + } + super.dispose(); + } } \ No newline at end of file diff --git a/src/vs/workbench/parts/preferences/browser/preferencesService.ts b/src/vs/workbench/parts/preferences/browser/preferencesService.ts index 10f314e0a27..9b80dabbbe1 100644 --- a/src/vs/workbench/parts/preferences/browser/preferencesService.ts +++ b/src/vs/workbench/parts/preferences/browser/preferencesService.ts @@ -146,29 +146,29 @@ export class PreferencesService extends Disposable implements IPreferencesServic } if (this.workspaceConfigSettingsResource.fsPath === uri.fsPath) { - promise = this.createEditableSettingsEditorModel(ConfigurationTarget.WORKSPACE); + promise = this.createEditableSettingsEditorModel(ConfigurationTarget.WORKSPACE, uri); this.defaultPreferencesEditorModels.set(uri, promise); return promise; } if (this.getEditableSettingsURI(ConfigurationTarget.USER).fsPath === uri.fsPath) { - return this.createEditableSettingsEditorModel(ConfigurationTarget.USER); + return this.createEditableSettingsEditorModel(ConfigurationTarget.USER, uri); } const workspaceSettingsUri = this.getEditableSettingsURI(ConfigurationTarget.WORKSPACE); if (workspaceSettingsUri && workspaceSettingsUri.fsPath === uri.fsPath) { - return this.createEditableSettingsEditorModel(ConfigurationTarget.WORKSPACE); + return this.createEditableSettingsEditorModel(ConfigurationTarget.WORKSPACE, workspaceSettingsUri); + } + + if (this.contextService.hasMultiFolderWorkspace()) { + return this.createEditableSettingsEditorModel(ConfigurationTarget.FOLDER, uri); } return TPromise.wrap>(null); } - openSettings(target: ConfigurationTarget | URI): TPromise { - return this.doOpenSettings(target); - } - openGlobalSettings(): TPromise { - return this.doOpenSettings(ConfigurationTarget.USER); + return this.doOpenSettings(ConfigurationTarget.USER, this.userSettingsResource); } openWorkspaceSettings(): TPromise { @@ -176,16 +176,20 @@ export class PreferencesService extends Disposable implements IPreferencesServic 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); + return this.doOpenSettings(ConfigurationTarget.WORKSPACE, this.workspaceSettingsResource); } - switchSettings(target: URI | ConfigurationTarget): TPromise { + openFolderSettings(folder: URI): TPromise { + return this.doOpenSettings(ConfigurationTarget.FOLDER, this.getEditableSettingsURI(ConfigurationTarget.FOLDER, folder)); + } + + switchSettings(target: ConfigurationTarget, resource: URI): TPromise { const activeEditor = this.editorService.getActiveEditor(); const activeEditorInput = activeEditor.input; if (activeEditorInput instanceof PreferencesEditorInput) { - return this.getOrCreateEditableSettingsEditorInput(target) + return this.getOrCreateEditableSettingsEditorInput(target, this.getEditableSettingsURI(target, resource)) .then(toInput => { - const replaceWith = new PreferencesEditorInput(this.getPreferencesEditorInputName(target), toInput.getDescription(), this.instantiationService.createInstance(DefaultPreferencesEditorInput, this.defaultSettingsResource), toInput); + const replaceWith = new PreferencesEditorInput(this.getPreferencesEditorInputName(target, resource), toInput.getDescription(), this.instantiationService.createInstance(DefaultPreferencesEditorInput, this.defaultSettingsResource), toInput); return this.editorService.replaceEditors([{ toReplace: this.lastOpenedSettingsInput, replaceWith @@ -194,7 +198,7 @@ export class PreferencesService extends Disposable implements IPreferencesServic }); }); } else { - this.openSettings(target); + this.doOpenSettings(target, resource); return undefined; } } @@ -231,13 +235,13 @@ export class PreferencesService extends Disposable implements IPreferencesServic }); } - private doOpenSettings(configurationTarget: ConfigurationTarget | URI): TPromise { + private doOpenSettings(configurationTarget: ConfigurationTarget, resource: URI): TPromise { const openDefaultSettings = !!this.configurationService.getConfiguration().workbench.settings.openDefaultSettings; - return this.getOrCreateEditableSettingsEditorInput(configurationTarget) + return this.getOrCreateEditableSettingsEditorInput(configurationTarget, resource) .then(editableSettingsEditorInput => { if (openDefaultSettings) { const defaultPreferencesEditorInput = this.instantiationService.createInstance(DefaultPreferencesEditorInput, this.defaultSettingsResource); - const preferencesEditorInput = new PreferencesEditorInput(this.getPreferencesEditorInputName(configurationTarget), editableSettingsEditorInput.getDescription(), defaultPreferencesEditorInput, editableSettingsEditorInput); + const preferencesEditorInput = new PreferencesEditorInput(this.getPreferencesEditorInputName(configurationTarget, resource), editableSettingsEditorInput.getDescription(), defaultPreferencesEditorInput, editableSettingsEditorInput); this.lastOpenedSettingsInput = preferencesEditorInput; return this.editorService.openEditor(preferencesEditorInput, { pinned: true }); } @@ -245,22 +249,18 @@ export class PreferencesService extends Disposable implements IPreferencesServic }); } - private getPreferencesEditorInputName(target: ConfigurationTarget | URI): string { - const name = getSettingsTargetName(target, this.contextService); - return target instanceof URI ? nls.localize('folderSettingsName', "{0} (Folder Settings)", name) : name; + 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; } - private getOrCreateEditableSettingsEditorInput(target: ConfigurationTarget | URI): TPromise { - const resource = this.getEditableSettingsURI(target); - if (resource) { - return this.createSettingsIfNotExists(target) - .then(() => this.editorService.createInput({ resource })); - } - return TPromise.wrapError(new Error('Unknown target ' + (target instanceof URI ? target.toString(false) : target.toString()))); + private getOrCreateEditableSettingsEditorInput(target: ConfigurationTarget, resource: URI): TPromise { + return this.createSettingsIfNotExists(target, resource) + .then(() => this.editorService.createInput({ resource })); } - private createEditableSettingsEditorModel(configurationTarget: ConfigurationTarget): TPromise { - const settingsUri = this.getEditableSettingsURI(configurationTarget); + private createEditableSettingsEditorModel(configurationTarget: ConfigurationTarget, resource: URI): TPromise { + 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)]) @@ -285,7 +285,7 @@ export class PreferencesService extends Disposable implements IPreferencesServic return TPromise.as(null); } - private getEmptyEditableSettingsContent(target: ConfigurationTarget | URI): string { + private getEmptyEditableSettingsContent(target: ConfigurationTarget): string { if (target === ConfigurationTarget.USER) { const emptySettingsHeader = nls.localize('emptySettingsHeader', "Place your settings in this file to overwrite the default settings"); return '// ' + emptySettingsHeader + '\n{\n}'; @@ -297,11 +297,7 @@ export class PreferencesService extends Disposable implements IPreferencesServic ].join('\n'); } - private getEditableSettingsURI(configurationTarget: ConfigurationTarget | URI): URI { - if (configurationTarget instanceof URI) { - const root = this.contextService.getRoot(configurationTarget); - return root ? this.toResource(paths.join('.vscode', 'settings.json'), root) : null; - } + private getEditableSettingsURI(configurationTarget: ConfigurationTarget, resource?: URI): URI { switch (configurationTarget) { case ConfigurationTarget.USER: return URI.file(this.environmentService.appSettingsPath); @@ -314,6 +310,9 @@ export class PreferencesService extends Disposable implements IPreferencesServic return this.workspaceConfigSettingsResource; } return null; + case ConfigurationTarget.FOLDER: + const root = this.contextService.getRoot(resource); + return root ? this.toResource(paths.join('.vscode', 'settings.json'), root) : null; } return null; } @@ -322,8 +321,7 @@ export class PreferencesService extends Disposable implements IPreferencesServic return URI.file(paths.join(root.fsPath, relativePath)); } - private createSettingsIfNotExists(target: ConfigurationTarget | URI): TPromise { - const resource = this.getEditableSettingsURI(target); + private createSettingsIfNotExists(target: ConfigurationTarget, resource: URI): TPromise { if (this.contextService.hasMultiFolderWorkspace() && target === ConfigurationTarget.WORKSPACE) { return TPromise.as(null); } diff --git a/src/vs/workbench/parts/preferences/browser/preferencesWidgets.ts b/src/vs/workbench/parts/preferences/browser/preferencesWidgets.ts index 94abdd28880..bce0560ebaa 100644 --- a/src/vs/workbench/parts/preferences/browser/preferencesWidgets.ts +++ b/src/vs/workbench/parts/preferences/browser/preferencesWidgets.ts @@ -257,12 +257,12 @@ export class SettingsTargetsWidget extends Widget { private targetLabel: HTMLSelectElement; private targetDetails: HTMLSelectElement; - private _onDidTargetChange: Emitter = new Emitter(); - public readonly onDidTargetChange: Event = this._onDidTargetChange.event; + private _onDidTargetChange: Emitter = new Emitter(); + public readonly onDidTargetChange: Event = this._onDidTargetChange.event; private borderColor: Color; - constructor(parent: HTMLElement, private target: ConfigurationTarget | URI, + constructor(parent: HTMLElement, private uri: URI, private target: ConfigurationTarget, @IWorkspaceContextService private workspaceContextService: IWorkspaceContextService, @IPreferencesService private preferencesService: IPreferencesService, @IContextMenuService private contextMenuService: IContextMenuService, @@ -276,7 +276,8 @@ export class SettingsTargetsWidget extends Widget { })); } - public setTarget(target: ConfigurationTarget | URI): void { + public setTarget(uri: URI, target: ConfigurationTarget): void { + this.uri = uri; this.target = target; this.updateLabel(); } @@ -290,7 +291,7 @@ export class SettingsTargetsWidget extends Widget { this.targetDetails = DOM.append(targetElement, DOM.$('.settings-target-details')); this.updateLabel(); - this.onclick(this.settingsTargetsContainer, e => this.showContextMennu(e)); + this.onclick(this.settingsTargetsContainer, e => this.showContextMenu(e)); DOM.append(this.settingsTargetsContainer, DOM.$('.settings-target-dropdown-icon.octicon.octicon-triangle-down')); @@ -298,13 +299,13 @@ export class SettingsTargetsWidget extends Widget { } private updateLabel(): void { - this.targetLabel.textContent = getSettingsTargetName(this.target, this.workspaceContextService); - const details = this.target instanceof URI ? localize('folderSettingsDetails', "Folder Settings") : ''; + this.targetLabel.textContent = getSettingsTargetName(this.target, this.uri, this.workspaceContextService); + const details = ConfigurationTarget.FOLDER === this.target ? localize('folderSettingsDetails', "Folder Settings") : ''; this.targetDetails.textContent = details; DOM.toggleClass(this.targetDetails, 'empty', !details); } - private showContextMennu(event: IMouseEvent): void { + private showContextMenu(event: IMouseEvent): void { const actions = this.getSettingsTargetsActions(); this.contextMenuService.showContextMenu({ getAnchor: () => this.settingsTargetsContainer, @@ -316,21 +317,23 @@ export class SettingsTargetsWidget extends Widget { private getSettingsTargetsActions(): IAction[] { const actions: IAction[] = []; + const userSettingsResource = this.preferencesService.userSettingsResource; actions.push({ id: 'userSettingsTarget', - label: getSettingsTargetName(ConfigurationTarget.USER, this.workspaceContextService), - checked: this.target === ConfigurationTarget.USER, + label: getSettingsTargetName(ConfigurationTarget.USER, userSettingsResource, this.workspaceContextService), + checked: this.uri.fsPath === userSettingsResource.fsPath, enabled: true, - run: () => this.onTargetClicked(ConfigurationTarget.USER) + run: () => this.onTargetClicked(userSettingsResource) }); if (this.workspaceContextService.hasWorkspace()) { + const workspaceSettingsResource = this.preferencesService.workspaceSettingsResource; actions.push({ id: 'workspaceSettingsTarget', - label: getSettingsTargetName(ConfigurationTarget.WORKSPACE, this.workspaceContextService), - checked: this.target === ConfigurationTarget.WORKSPACE, + label: getSettingsTargetName(ConfigurationTarget.WORKSPACE, workspaceSettingsResource, this.workspaceContextService), + checked: this.uri.fsPath === workspaceSettingsResource.fsPath, enabled: true, - run: () => this.onTargetClicked(ConfigurationTarget.WORKSPACE) + run: () => this.onTargetClicked(workspaceSettingsResource) }); } @@ -339,8 +342,8 @@ export class SettingsTargetsWidget extends Widget { actions.push(...this.workspaceContextService.getWorkspace().roots.map((root, index) => { return { id: 'folderSettingsTarget' + index, - label: getSettingsTargetName(root, this.workspaceContextService), - checked: this.target instanceof URI && this.target.fsPath === root.fsPath, + label: getSettingsTargetName(ConfigurationTarget.FOLDER, root, this.workspaceContextService), + checked: this.uri instanceof URI && this.uri.fsPath === root.fsPath, enabled: true, run: () => this.onTargetClicked(root) }; @@ -350,11 +353,8 @@ export class SettingsTargetsWidget extends Widget { return actions; } - private onTargetClicked(target: ConfigurationTarget | URI): void { - if (this.target instanceof URI && target instanceof URI && this.target.fsPath === target.fsPath) { - return; - } - if (this.target === target) { + private onTargetClicked(target: URI): void { + if (this.uri.fsPath === target.fsPath) { return; } this._onDidTargetChange.fire(target); diff --git a/src/vs/workbench/parts/preferences/common/preferences.ts b/src/vs/workbench/parts/preferences/common/preferences.ts index e076d37b2de..78823a00c28 100644 --- a/src/vs/workbench/parts/preferences/common/preferences.ts +++ b/src/vs/workbench/parts/preferences/common/preferences.ts @@ -76,10 +76,10 @@ export interface IPreferencesService { resolveContent(uri: URI): TPromise; createPreferencesEditorModel(uri: URI): TPromise>; - openSettings(target: ConfigurationTarget | URI): TPromise; - switchSettings(target: URI | ConfigurationTarget): TPromise; openGlobalSettings(): TPromise; openWorkspaceSettings(): TPromise; + openFolderSettings(folder: URI): TPromise; + switchSettings(target: ConfigurationTarget, resource: URI): TPromise; openGlobalKeybindingSettings(textual: boolean): TPromise; configureSettingsForLanguage(language: string): void; @@ -99,16 +99,15 @@ export interface IKeybindingsEditor extends IEditor { showConflicts(keybindingEntry: IKeybindingItemEntry): TPromise; } -export function getSettingsTargetName(target: ConfigurationTarget | URI, workspaceContextService: IWorkspaceContextService): string { - if (target instanceof URI) { - const root = workspaceContextService.getRoot(target); - return root ? paths.basename(root.fsPath) : ''; - } +export function getSettingsTargetName(target: ConfigurationTarget, resource: URI, workspaceContextService: IWorkspaceContextService): string { switch (target) { case ConfigurationTarget.USER: return localize('userSettingsTarget', "User Settings"); case ConfigurationTarget.WORKSPACE: return localize('workspaceSettingsTarget', "Workspace Settings"); + case ConfigurationTarget.FOLDER: + const root = workspaceContextService.getRoot(resource); + return root ? paths.basename(root.fsPath) : ''; } } diff --git a/src/vs/workbench/services/configuration/common/configurationEditing.ts b/src/vs/workbench/services/configuration/common/configurationEditing.ts index 415e2e58d9e..96e9fb440da 100644 --- a/src/vs/workbench/services/configuration/common/configurationEditing.ts +++ b/src/vs/workbench/services/configuration/common/configurationEditing.ts @@ -17,6 +17,11 @@ export enum ConfigurationEditingErrorCode { */ ERROR_UNKNOWN_KEY, + /** + * Error when trying to write a configuration key that is not supported for provided target. + */ + ERROR_INVALID_KEY, + /** * Error when trying to write to user target but not supported for provided key. */ @@ -54,7 +59,12 @@ export enum ConfigurationTarget { /** * Targets the workspace configuration file for writing. This only works if a workspace is opened. */ - WORKSPACE + WORKSPACE, + + /** + * Targets the folder configuration file for writing. This only works if a workspace is opened. + */ + FOLDER } export interface IConfigurationValue { diff --git a/src/vs/workbench/services/configuration/node/configurationEditingService.ts b/src/vs/workbench/services/configuration/node/configurationEditingService.ts index f6da2cfebb7..b4fabc97b0f 100644 --- a/src/vs/workbench/services/configuration/node/configurationEditingService.ts +++ b/src/vs/workbench/services/configuration/node/configurationEditingService.ts @@ -18,6 +18,7 @@ import { Edit } from 'vs/base/common/jsonFormatter'; import { IReference } from 'vs/base/common/lifecycle'; import * as editorCommon from 'vs/editor/common/editorCommon'; import { EditOperation } from 'vs/editor/common/core/editOperation'; +import { Registry } from 'vs/platform/registry/common/platform'; import { Range } from 'vs/editor/common/core/range'; import { Selection } from 'vs/editor/common/core/selection'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; @@ -29,7 +30,7 @@ import { WORKSPACE_CONFIG_DEFAULT_PATH, WORKSPACE_STANDALONE_CONFIGURATIONS } fr import { IFileService } from 'vs/platform/files/common/files'; import { IConfigurationEditingService, ConfigurationEditingErrorCode, ConfigurationEditingError, ConfigurationTarget, IConfigurationValue, IConfigurationEditingOptions } from 'vs/workbench/services/configuration/common/configurationEditing'; import { ITextModelService, ITextEditorModel } from 'vs/editor/common/services/resolverService'; -import { OVERRIDE_PROPERTY_PATTERN } from 'vs/platform/configuration/common/configurationRegistry'; +import { OVERRIDE_PROPERTY_PATTERN, IConfigurationRegistry, Extensions as ConfigurationExtensions, ConfigurationScope } from 'vs/platform/configuration/common/configurationRegistry'; import { IChoiceService, IMessageService, Severity } from 'vs/platform/message/common/message'; import { ICommandService } from 'vs/platform/commands/common/commands'; @@ -84,7 +85,7 @@ export class ConfigurationEditingService implements IConfigurationEditingService const checkDirtyConfiguration = !(options.force || options.donotSave); const saveConfiguration = options.force || !options.donotSave; - return this.resolveAndValidate(target, operation, checkDirtyConfiguration) + return this.resolveAndValidate(target, operation, checkDirtyConfiguration, options.scopes || {}) .then(reference => this.writeToBuffer(reference.object.textEditorModel, operation, saveConfiguration)); } @@ -163,10 +164,11 @@ export class ConfigurationEditingService implements IConfigurationEditingService // API constraints case ConfigurationEditingErrorCode.ERROR_UNKNOWN_KEY: return nls.localize('errorUnknownKey', "Unable to write to the configuration file (Unknown Key)"); + case ConfigurationEditingErrorCode.ERROR_INVALID_KEY: return nls.localize('errorInvalidKey', "Unable to write to the configuration file (Invalid Key)"); case ConfigurationEditingErrorCode.ERROR_INVALID_TARGET: return nls.localize('errorInvalidTarget', "Unable to write to the configuration file (Invalid Target)"); // User issues - case ConfigurationEditingErrorCode.ERROR_NO_WORKSPACE_OPENED: return nls.localize('errorNoWorkspaceOpened', "Unable to write into settings because no folder is opened. Please open a folder first and try again."); + case ConfigurationEditingErrorCode.ERROR_NO_WORKSPACE_OPENED: return nls.localize('errorNoWorkspaceOpened', "Unable to write into settings because no workspace is opened. Please open a workspace first and try again."); case ConfigurationEditingErrorCode.ERROR_INVALID_CONFIGURATION: { if (target === ConfigurationTarget.USER) { return nls.localize('errorInvalidConfiguration', "Unable to write into settings. Please open **User Settings** to correct errors/warnings in the file and try again."); @@ -221,7 +223,7 @@ export class ConfigurationEditingService implements IConfigurationEditingService return parseErrors.length > 0; } - private resolveAndValidate(target: ConfigurationTarget, operation: IConfigurationEditOperation, checkDirty: boolean): TPromise> { + private resolveAndValidate(target: ConfigurationTarget, operation: IConfigurationEditOperation, checkDirty: boolean, overrides: IConfigurationOverrides): TPromise> { // Any key must be a known setting from the registry (unless this is a standalone config) if (!operation.isWorkspaceStandalone) { @@ -236,11 +238,22 @@ export class ConfigurationEditingService implements IConfigurationEditingService return this.wrapError(ConfigurationEditingErrorCode.ERROR_INVALID_TARGET, target); } - // Target cannot be workspace if no workspace opened - if (target === ConfigurationTarget.WORKSPACE && !this.contextService.hasWorkspace()) { + // Target cannot be workspace or folder if no workspace opened + if ((target === ConfigurationTarget.WORKSPACE || target === ConfigurationTarget.FOLDER) && !this.contextService.hasWorkspace()) { return this.wrapError(ConfigurationEditingErrorCode.ERROR_NO_WORKSPACE_OPENED, target); } + if (target === ConfigurationTarget.FOLDER) { + if (!operation.resource) { + return this.wrapError(ConfigurationEditingErrorCode.ERROR_INVALID_TARGET, target); + } + + const configurationProperties = Registry.as(ConfigurationExtensions.Configuration).getConfigurationProperties(); + if (configurationProperties[operation.key].scope !== ConfigurationScope.RESOURCE) { + return this.wrapError(ConfigurationEditingErrorCode.ERROR_INVALID_KEY, target); + } + } + return this.resolveModelReference(operation.resource) .then(reference => { const model = reference.object.textEditorModel; @@ -266,7 +279,7 @@ export class ConfigurationEditingService implements IConfigurationEditingService const standaloneConfigurationKeys = Object.keys(WORKSPACE_STANDALONE_CONFIGURATIONS); for (let i = 0; i < standaloneConfigurationKeys.length; i++) { const key = standaloneConfigurationKeys[i]; - const resource = this.getConfigurationFileResource(WORKSPACE_STANDALONE_CONFIGURATIONS[key], overrides.resource); + const resource = this.getConfigurationFileResource(target, WORKSPACE_STANDALONE_CONFIGURATIONS[key], overrides.resource); // Check for prefix if (config.key === key) { @@ -289,23 +302,34 @@ export class ConfigurationEditingService implements IConfigurationEditingService return { key, jsonPath, value: config.value, resource: URI.file(this.environmentService.appSettingsPath) }; } - const resource = this.getConfigurationFileResource(WORKSPACE_CONFIG_DEFAULT_PATH, overrides.resource); + const resource = this.getConfigurationFileResource(target, WORKSPACE_CONFIG_DEFAULT_PATH, overrides.resource); if (workspace && workspace.configuration && resource && workspace.configuration.fsPath === resource.fsPath) { jsonPath = ['settings', ...jsonPath]; } return { key, jsonPath, value: config.value, resource }; } - private getConfigurationFileResource(relativePath: string, resource: URI): URI { + private getConfigurationFileResource(target: ConfigurationTarget, relativePath: string, resource: URI): URI { + if (target === ConfigurationTarget.USER) { + return URI.file(this.environmentService.appSettingsPath); + } + const workspace = this.contextService.getWorkspace(); + if (workspace) { - if (resource) { - const root = this.contextService.getRoot(resource); - if (root) { - return this.toResource(relativePath, root); + + if (target === ConfigurationTarget.WORKSPACE) { + return this.contextService.hasMultiFolderWorkspace() ? workspace.configuration : this.toResource(relativePath, workspace.roots[0]); + } + + if (target === ConfigurationTarget.FOLDER) { + if (resource) { + const root = this.contextService.getRoot(resource); + if (root) { + return this.toResource(relativePath, root); + } } } - return workspace.configuration || this.toResource(relativePath, workspace.roots[0]); } return null; } From 83289390585c4b72bdd0353cc8b5b303a18b969b Mon Sep 17 00:00:00 2001 From: Ramya Achutha Rao Date: Mon, 24 Jul 2017 10:58:44 -0700 Subject: [PATCH 013/136] Emmet update dependencies --- extensions/emmet/npm-shrinkwrap.json | 10 +++++----- extensions/emmet/package.json | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/extensions/emmet/npm-shrinkwrap.json b/extensions/emmet/npm-shrinkwrap.json index 0ac9d4b7f21..706f04d0359 100644 --- a/extensions/emmet/npm-shrinkwrap.json +++ b/extensions/emmet/npm-shrinkwrap.json @@ -48,9 +48,9 @@ "resolved": "https://registry.npmjs.org/@emmetio/html-snippets-resolver/-/html-snippets-resolver-0.1.4.tgz" }, "@emmetio/html-transform": { - "version": "0.3.2", + "version": "0.3.3", "from": "@emmetio/html-transform@>=0.3.2 <0.4.0", - "resolved": "https://registry.npmjs.org/@emmetio/html-transform/-/html-transform-0.3.2.tgz" + "resolved": "https://registry.npmjs.org/@emmetio/html-transform/-/html-transform-0.3.3.tgz" }, "@emmetio/implicit-tag": { "version": "1.0.0", @@ -123,9 +123,9 @@ "resolved": "https://registry.npmjs.org/image-size/-/image-size-0.5.5.tgz" }, "vscode-emmet-helper": { - "version": "0.0.28", - "from": "vscode-emmet-helper@0.0.28", - "resolved": "https://registry.npmjs.org/vscode-emmet-helper/-/vscode-emmet-helper-0.0.28.tgz" + "version": "0.0.29", + "from": "vscode-emmet-helper@0.0.29", + "resolved": "https://registry.npmjs.org/vscode-emmet-helper/-/vscode-emmet-helper-0.0.29.tgz" }, "vscode-languageserver-types": { "version": "3.3.0", diff --git a/extensions/emmet/package.json b/extensions/emmet/package.json index 32a82d36714..098e09e2e3c 100644 --- a/extensions/emmet/package.json +++ b/extensions/emmet/package.json @@ -74,7 +74,7 @@ "@emmetio/html-matcher": "^0.3.1", "@emmetio/css-parser": "^0.3.0", "@emmetio/math-expression": "^0.1.1", - "vscode-emmet-helper":"0.0.28", + "vscode-emmet-helper":"^0.0.29", "vscode-languageserver-types": "^3.0.3", "image-size": "^0.5.2" } From 283a790c883664d9c613455ff6309f09f6082741 Mon Sep 17 00:00:00 2001 From: Ramya Achutha Rao Date: Mon, 24 Jul 2017 12:01:43 -0700 Subject: [PATCH 014/136] Emmet: Reflect CSS Value in extension #31298 --- extensions/emmet/src/extension.ts | 6 ++ extensions/emmet/src/reflectCssValue.ts | 61 +++++++++++++++++++++ extensions/emmet/src/typings/EmmetNode.d.ts | 2 + extensions/emmet/src/updateImageSize.ts | 23 +++----- extensions/emmet/src/util.ts | 12 +++- 5 files changed, 87 insertions(+), 17 deletions(-) create mode 100644 extensions/emmet/src/reflectCssValue.ts diff --git a/extensions/emmet/src/extension.ts b/extensions/emmet/src/extension.ts index 0357ee05b2f..45118cb5cfb 100644 --- a/extensions/emmet/src/extension.ts +++ b/extensions/emmet/src/extension.ts @@ -20,6 +20,8 @@ import { incrementDecrement } from './incrementDecrement'; import { LANGUAGE_MODES, getMappingForIncludedLanguages } from './util'; import { updateExtensionsPath } from 'vscode-emmet-helper'; import { updateImageSize } from './updateImageSize'; +import { reflectCssValue } from './reflectCssValue'; + import * as path from 'path'; export function activate(context: vscode.ExtensionContext) { @@ -118,6 +120,10 @@ export function activate(context: vscode.ExtensionContext) { return updateImageSize(); })); + context.subscriptions.push(vscode.commands.registerCommand('emmet.reflectCssValue', () => { + return reflectCssValue(); + })); + let currentExtensionsPath = undefined; let resolveUpdateExtensionsPath = () => { let extensionsPath = vscode.workspace.getConfiguration('emmet')['extensionsPath']; diff --git a/extensions/emmet/src/reflectCssValue.ts b/extensions/emmet/src/reflectCssValue.ts new file mode 100644 index 00000000000..4aa1319df6e --- /dev/null +++ b/extensions/emmet/src/reflectCssValue.ts @@ -0,0 +1,61 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Range, window, TextEditor } from 'vscode'; +import { isStyleSheet } from 'vscode-emmet-helper'; +import { parse, getNode, getCssProperty } from './util'; +import { Property, Rule } from 'EmmetNode'; + +const vendorPrefixes = ['-webkit-', '-moz-', '-ms-', '-o-', '']; + +export function reflectCssValue() { + let editor = window.activeTextEditor; + if (!editor) { + window.showInformationMessage('No editor is active.'); + return; + } + + if (!isStyleSheet(editor.document.languageId)) { + return; + } + + const rootNode = parse(editor.document); + const node = getNode(rootNode, editor.selection.active, true); + if (!node || node.type !== 'property') { + return; + } + + return updateCSSNode(editor, node); + +} + +function updateCSSNode(editor: TextEditor, property: Property) { + const rule: Rule = property.parent; + let currentPrefix = ''; + + // Find vendor prefix of given property node + for (let i = 0; i < vendorPrefixes.length; i++) { + if (property.name.startsWith(vendorPrefixes[i])) { + currentPrefix = vendorPrefixes[i]; + break; + } + } + + const propertyName = property.name.substr(currentPrefix.length); + const propertyValue = property.value; + + return editor.edit(builder => { + // Find properties with vendor prefixes, update each + vendorPrefixes.forEach(prefix => { + if (prefix === currentPrefix) { + return; + } + let vendorProperty = getCssProperty(rule, prefix + propertyName); + if (vendorProperty) { + builder.replace(new Range(vendorProperty.valueToken.start, vendorProperty.valueToken.end), propertyValue); + } + }); + }); +} \ No newline at end of file diff --git a/extensions/emmet/src/typings/EmmetNode.d.ts b/extensions/emmet/src/typings/EmmetNode.d.ts index fc91a991e89..bf27b63ca91 100644 --- a/extensions/emmet/src/typings/EmmetNode.d.ts +++ b/extensions/emmet/src/typings/EmmetNode.d.ts @@ -52,6 +52,7 @@ declare module 'EmmetNode' { } export interface CssNode extends Node { + name: string parent: CssNode firstChild: CssNode nextSibling: CssNode @@ -70,6 +71,7 @@ declare module 'EmmetNode' { separator: Token parent: Rule terminatorToken: Token + value: string } export interface Stylesheet extends Node { diff --git a/extensions/emmet/src/updateImageSize.ts b/extensions/emmet/src/updateImageSize.ts index 350257f907d..713b9c1f8c3 100644 --- a/extensions/emmet/src/updateImageSize.ts +++ b/extensions/emmet/src/updateImageSize.ts @@ -11,7 +11,7 @@ import { TextEditor, Range, Position, window } from 'vscode'; import * as path from 'path'; import { getImageSize } from './imageSizeHelper'; import { isStyleSheet } from 'vscode-emmet-helper'; -import { parse, getNode, iterateCSSToken } from './util'; +import { parse, getNode, iterateCSSToken, getCssProperty } from './util'; import { HtmlNode, CssToken, HtmlToken, Attribute, Property } from 'EmmetNode'; import { locateFile } from './locateFile'; import parseStylesheet from '@emmetio/css-parser'; @@ -213,12 +213,12 @@ function updateHTMLTag(editor: TextEditor, node: HtmlNode, width: number, height */ function updateCSSNode(editor: TextEditor, srcProp: Property, width: number, height: number) { const rule = srcProp.parent; - const widthProp = getProperty(rule, 'width'); - const heightProp = getProperty(rule, 'height'); + const widthProp = getCssProperty(rule, 'width'); + const heightProp = getCssProperty(rule, 'height'); // Detect formatting const separator = srcProp.separator || ': '; - const before = getBefore(editor, srcProp); + const before = getPropertyDelimitor(editor, srcProp); let edits: [Range, string][] = []; if (!srcProp.terminatorToken) { @@ -291,23 +291,13 @@ function findUrlToken(node, pos: Position) { } } -/** - * Returns `name` CSS property from given `rule` - * @param {Node} rule - * @param {String} name - * @return {Node} - */ -function getProperty(rule, name) { - return rule.children.find(node => node.type === 'property' && node.name === name); -} - /** * Returns a string that is used to delimit properties in current node’s rule * @param {TextEditor} editor - * @param {Node} node + * @param {Property} node * @return {String} */ -function getBefore(editor: TextEditor, node: Property) { +function getPropertyDelimitor(editor: TextEditor, node: Property) { let anchor; if (anchor = (node.previousSibling || node.parent.contentStartToken)) { return editor.document.getText(new Range(anchor.end, node.start)); @@ -317,3 +307,4 @@ function getBefore(editor: TextEditor, node: Property) { return ''; } + diff --git a/extensions/emmet/src/util.ts b/extensions/emmet/src/util.ts index 17d67649a6a..a0b8900d385 100644 --- a/extensions/emmet/src/util.ts +++ b/extensions/emmet/src/util.ts @@ -6,7 +6,7 @@ import * as vscode from 'vscode'; import parse from '@emmetio/html-matcher'; import parseStylesheet from '@emmetio/css-parser'; -import { Node, HtmlNode, CssToken } from 'EmmetNode'; +import { Node, HtmlNode, CssToken, Property } from 'EmmetNode'; import { DocumentStreamReader } from './bufferStream'; import { isStyleSheet } from 'vscode-emmet-helper'; @@ -276,3 +276,13 @@ export function iterateCSSToken(token: CssToken, fn) { } } } + +/** + * Returns `name` CSS property from given `rule` + * @param {Node} rule + * @param {String} name + * @return {Property} + */ +export function getCssProperty(rule, name): Property { + return rule.children.find(node => node.type === 'property' && node.name === name); +} From 244d632da0a3042417370cf5fa332b83b1f8891b Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Mon, 24 Jul 2017 13:14:20 -0700 Subject: [PATCH 015/136] Fix search smoke test and remove "in 1 folder" message in non-multiroot mode. Fix #31304 --- .../workbench/parts/search/browser/searchViewlet.ts | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/parts/search/browser/searchViewlet.ts b/src/vs/workbench/parts/search/browser/searchViewlet.ts index 18df36545b5..e8806b4501c 100644 --- a/src/vs/workbench/parts/search/browser/searchViewlet.ts +++ b/src/vs/workbench/parts/search/browser/searchViewlet.ts @@ -1178,7 +1178,17 @@ export class SearchViewlet extends Viewlet { } private buildResultCountMessage(resultCount: number, fileCount: number, folderCount: number): string { - if (folderCount === 1) { + if (!this.contextService.hasMultiFolderWorkspace()) { + if (resultCount === 1 && fileCount === 1) { + return nls.localize('search.file.result', "{0} result in {1} file", resultCount, fileCount); + } else if (resultCount === 1) { + return nls.localize('search.files.result', "{0} result in {1} files", resultCount, fileCount); + } else if (fileCount === 1) { + return nls.localize('search.file.results', "{0} results in {1} file", resultCount, fileCount); + } else { + return nls.localize('search.files.results', "{0} results in {1} files", resultCount, fileCount); + } + } else if (folderCount === 1) { if (resultCount === 1 && fileCount === 1) { return nls.localize('search.folder.file.result', "{0} result in {1} file in {2} folder", resultCount, fileCount, folderCount); } else if (resultCount === 1) { From 58332b486e7e5196f4b21edc93081686c0509e55 Mon Sep 17 00:00:00 2001 From: Ramya Achutha Rao Date: Mon, 24 Jul 2017 13:28:14 -0700 Subject: [PATCH 016/136] Route ReflectCSSValue command to emmet extension --- src/vs/workbench/parts/emmet/electron-browser/emmetActions.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/parts/emmet/electron-browser/emmetActions.ts b/src/vs/workbench/parts/emmet/electron-browser/emmetActions.ts index c13cae1fe39..12ca2b1098c 100644 --- a/src/vs/workbench/parts/emmet/electron-browser/emmetActions.ts +++ b/src/vs/workbench/parts/emmet/electron-browser/emmetActions.ts @@ -258,7 +258,8 @@ export abstract class EmmetEditorAction extends EditorAction { 'editor.emmet.action.decrementNumberByOneTenth': 'emmet.decrementNumberByOneTenth', 'editor.emmet.action.decrementNumberByOne': 'emmet.decrementNumberByOne', 'editor.emmet.action.decrementNumberByTen': 'emmet.decrementNumberByTen', - 'editor.emmet.action.updateImageSize': 'emmet.updateImageSize' + 'editor.emmet.action.updateImageSize': 'emmet.updateImageSize', + 'editor.emmet.action.reflectCssValue': 'emmet.reflectCssValue' }; protected emmetActionName: string; From 0b1f37600b3e5ce24b6f4a150150eaee36f2c8e5 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Mon, 24 Jul 2017 13:38:57 -0700 Subject: [PATCH 017/136] Fix tsconfig provider not setting workspace for active file --- extensions/typescript/src/features/taskProvider.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions/typescript/src/features/taskProvider.ts b/extensions/typescript/src/features/taskProvider.ts index 3a34db0924b..e65c8e3e87e 100644 --- a/extensions/typescript/src/features/taskProvider.ts +++ b/extensions/typescript/src/features/taskProvider.ts @@ -107,7 +107,7 @@ class TscTaskProvider implements vscode.TaskProvider { const { configFileName } = res.body; if (configFileName && !isImplicitProjectConfigFile(configFileName)) { - const path = vscode.Uri.parse(configFileName); + const path = vscode.Uri.file(configFileName); const folder = vscode.workspace.getWorkspaceFolder(path); return [{ path: configFileName, From 5d33b200bd4a7a4a554afa3d03dabd997d484c92 Mon Sep 17 00:00:00 2001 From: Ramya Achutha Rao Date: Mon, 24 Jul 2017 14:03:55 -0700 Subject: [PATCH 018/136] Commenting emmet integration tests --- scripts/test-integration.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/test-integration.sh b/scripts/test-integration.sh index 28d9581c189..b139374725d 100755 --- a/scripts/test-integration.sh +++ b/scripts/test-integration.sh @@ -16,6 +16,6 @@ cd $ROOT ./scripts/code.sh $ROOT/extensions/vscode-api-tests/testWorkspace --extensionDevelopmentPath=$ROOT/extensions/vscode-api-tests --extensionTestsPath=$ROOT/extensions/vscode-api-tests/out --disableExtensions --user-data-dir=$VSCODEUSERDATADIR ./scripts/code.sh $ROOT/extensions/vscode-colorize-tests/test --extensionDevelopmentPath=$ROOT/extensions/vscode-colorize-tests --extensionTestsPath=$ROOT/extensions/vscode-colorize-tests/out --user-data-dir=$VSCODEUSERDATADIR ./scripts/test-int-mocha.sh -./scripts/code.sh --extensionDevelopmentPath=$ROOT/extensions/emmet --extensionTestsPath=$ROOT/extensions/emmet/out/test --disableExtensions --user-data-dir=$VSCODEUSERDATADIR +#./scripts/code.sh --extensionDevelopmentPath=$ROOT/extensions/emmet --extensionTestsPath=$ROOT/extensions/emmet/out/test --disableExtensions --user-data-dir=$VSCODEUSERDATADIR rm -r $VSCODEUSERDATADIR From 01e3164faf0b7db4fb361e3bb0295ad96596c4ed Mon Sep 17 00:00:00 2001 From: Ramya Achutha Rao Date: Mon, 24 Jul 2017 14:11:41 -0700 Subject: [PATCH 019/136] Fixing broken integration tests --- scripts/test-integration.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/test-integration.sh b/scripts/test-integration.sh index b139374725d..d0154a4101d 100755 --- a/scripts/test-integration.sh +++ b/scripts/test-integration.sh @@ -16,6 +16,6 @@ cd $ROOT ./scripts/code.sh $ROOT/extensions/vscode-api-tests/testWorkspace --extensionDevelopmentPath=$ROOT/extensions/vscode-api-tests --extensionTestsPath=$ROOT/extensions/vscode-api-tests/out --disableExtensions --user-data-dir=$VSCODEUSERDATADIR ./scripts/code.sh $ROOT/extensions/vscode-colorize-tests/test --extensionDevelopmentPath=$ROOT/extensions/vscode-colorize-tests --extensionTestsPath=$ROOT/extensions/vscode-colorize-tests/out --user-data-dir=$VSCODEUSERDATADIR ./scripts/test-int-mocha.sh -#./scripts/code.sh --extensionDevelopmentPath=$ROOT/extensions/emmet --extensionTestsPath=$ROOT/extensions/emmet/out/test --disableExtensions --user-data-dir=$VSCODEUSERDATADIR +./scripts/code.sh $ROOT/extensions/emmet/test-fixtures --extensionDevelopmentPath=$ROOT/extensions/emmet --extensionTestsPath=$ROOT/extensions/emmet/out/test --disableExtensions --user-data-dir=$VSCODEUSERDATADIR rm -r $VSCODEUSERDATADIR From 5db74cd3de5fd6a9283198ce4892085d3a3918d0 Mon Sep 17 00:00:00 2001 From: Ramya Achutha Rao Date: Mon, 24 Jul 2017 14:20:52 -0700 Subject: [PATCH 020/136] Tests for Emmet Reflect CSS Value in css files --- .../emmet/src/test/incrementDecrement.test.ts | 72 +++++++++---------- .../emmet/src/test/reflectCssValue.test.ts | 37 ++++++++++ extensions/emmet/src/test/testUtils.ts | 8 +-- 3 files changed, 77 insertions(+), 40 deletions(-) create mode 100644 extensions/emmet/src/test/reflectCssValue.test.ts diff --git a/extensions/emmet/src/test/incrementDecrement.test.ts b/extensions/emmet/src/test/incrementDecrement.test.ts index 9aace12eb3d..e56a8126819 100644 --- a/extensions/emmet/src/test/incrementDecrement.test.ts +++ b/extensions/emmet/src/test/incrementDecrement.test.ts @@ -17,62 +17,62 @@ suite('Tests for Increment/Decrement Emmet Commands', () => { `; test('incrementNumberByOne', function (): any { - return withRandomFileEditor(contents, (editor, doc) => { - editor.selections = [new Selection(1, 7, 1, 10), new Selection(2, 7, 2, 10)]; - return commands.executeCommand('emmet.incrementNumberByOne').then(() => { - assert.equal(doc.getText(), contents.replace('123', '124').replace('999', '1000')); - return Promise.resolve(); - }); + return withRandomFileEditor(contents, 'txt', (editor, doc) => { + editor.selections = [new Selection(1, 7, 1, 10), new Selection(2, 7, 2, 10)]; + return commands.executeCommand('emmet.incrementNumberByOne').then(() => { + assert.equal(doc.getText(), contents.replace('123', '124').replace('999', '1000')); + return Promise.resolve(); }); + }); }); test('incrementNumberByTen', function (): any { - return withRandomFileEditor(contents, (editor, doc) => { - editor.selections = [new Selection(1, 7, 1, 10), new Selection(2, 7, 2, 10)]; - return commands.executeCommand('emmet.incrementNumberByTen').then(() => { - assert.equal(doc.getText(), contents.replace('123', '133').replace('999', '1009')); - return Promise.resolve(); - }); + return withRandomFileEditor(contents, 'txt', (editor, doc) => { + editor.selections = [new Selection(1, 7, 1, 10), new Selection(2, 7, 2, 10)]; + return commands.executeCommand('emmet.incrementNumberByTen').then(() => { + assert.equal(doc.getText(), contents.replace('123', '133').replace('999', '1009')); + return Promise.resolve(); }); + }); }); test('incrementNumberByOneTenth', function (): any { - return withRandomFileEditor(contents, (editor, doc) => { - editor.selections = [new Selection(1, 7, 1, 13), new Selection(2, 7, 2, 12)]; - return commands.executeCommand('emmet.incrementNumberByOneTenth').then(() => { - assert.equal(doc.getText(), contents.replace('123.43', '123.53').replace('999.9', '1000')); - return Promise.resolve(); - }); + return withRandomFileEditor(contents, 'txt', (editor, doc) => { + editor.selections = [new Selection(1, 7, 1, 13), new Selection(2, 7, 2, 12)]; + return commands.executeCommand('emmet.incrementNumberByOneTenth').then(() => { + assert.equal(doc.getText(), contents.replace('123.43', '123.53').replace('999.9', '1000')); + return Promise.resolve(); }); + }); }); test('decrementNumberByOne', function (): any { - return withRandomFileEditor(contents, (editor, doc) => { - editor.selections = [new Selection(1, 7, 1, 10), new Selection(3, 7, 3, 10)]; - return commands.executeCommand('emmet.decrementNumberByOne').then(() => { - assert.equal(doc.getText(), contents.replace('123', '122').replace('100', '99')); - return Promise.resolve(); - }); + return withRandomFileEditor(contents, 'txt', (editor, doc) => { + editor.selections = [new Selection(1, 7, 1, 10), new Selection(3, 7, 3, 10)]; + return commands.executeCommand('emmet.decrementNumberByOne').then(() => { + assert.equal(doc.getText(), contents.replace('123', '122').replace('100', '99')); + return Promise.resolve(); }); + }); }); test('decrementNumberByTen', function (): any { - return withRandomFileEditor(contents, (editor, doc) => { - editor.selections = [new Selection(1, 7, 1, 10), new Selection(3, 7, 3, 10)]; - return commands.executeCommand('emmet.decrementNumberByTen').then(() => { - assert.equal(doc.getText(), contents.replace('123', '113').replace('100', '90')); - return Promise.resolve(); - }); + return withRandomFileEditor(contents, 'txt', (editor, doc) => { + editor.selections = [new Selection(1, 7, 1, 10), new Selection(3, 7, 3, 10)]; + return commands.executeCommand('emmet.decrementNumberByTen').then(() => { + assert.equal(doc.getText(), contents.replace('123', '113').replace('100', '90')); + return Promise.resolve(); }); + }); }); test('decrementNumberByOneTenth', function (): any { - return withRandomFileEditor(contents, (editor, doc) => { - editor.selections = [new Selection(1, 7, 1, 13), new Selection(3, 7, 3, 10)]; - return commands.executeCommand('emmet.decrementNumberByOneTenth').then(() => { - assert.equal(doc.getText(), contents.replace('123.43', '123.33').replace('100', '99.9')); - return Promise.resolve(); - }); + return withRandomFileEditor(contents, 'txt', (editor, doc) => { + editor.selections = [new Selection(1, 7, 1, 13), new Selection(3, 7, 3, 10)]; + return commands.executeCommand('emmet.decrementNumberByOneTenth').then(() => { + assert.equal(doc.getText(), contents.replace('123.43', '123.33').replace('100', '99.9')); + return Promise.resolve(); }); + }); }); }); \ No newline at end of file diff --git a/extensions/emmet/src/test/reflectCssValue.test.ts b/extensions/emmet/src/test/reflectCssValue.test.ts new file mode 100644 index 00000000000..fdc7f8b422a --- /dev/null +++ b/extensions/emmet/src/test/reflectCssValue.test.ts @@ -0,0 +1,37 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as assert from 'assert'; +import { Selection, commands } from 'vscode'; +import { withRandomFileEditor, closeAllEditors } from './testUtils'; + +suite('Tests for Emmet: Reflect CSS Value command', () => { + teardown(closeAllEditors); + + const contents = ` + .header { + margin: 10px; + padding: 10px; + transform: rotate(50deg); + -moz-transform: rotate(20deg); + -o-transform: rotate(50deg); + -webkit-transform: rotate(50deg); + -ms-transform: rotate(50deg); + } + `; + + test('reflectCssValue', function (): any { + + return withRandomFileEditor(contents, '.css', (editor, doc) => { + editor.selections = [new Selection(5, 10, 5, 10)]; + return commands.executeCommand('emmet.reflectCssValue').then(() => { + assert.equal(doc.getText(), contents.replace(/\(50deg\)/g, '(20deg)')); + return Promise.resolve(); + }); + }); + }); + + +}); \ No newline at end of file diff --git a/extensions/emmet/src/test/testUtils.ts b/extensions/emmet/src/test/testUtils.ts index 3548946b570..c90691c0916 100644 --- a/extensions/emmet/src/test/testUtils.ts +++ b/extensions/emmet/src/test/testUtils.ts @@ -14,9 +14,9 @@ function rndName() { return Math.random().toString(36).replace(/[^a-z]+/g, '').substr(0, 10); } -export function createRandomFile(contents = ''): Thenable { +export function createRandomFile(contents = '', fileExtension = 'txt'): Thenable { return new Promise((resolve, reject) => { - const tmpFile = join(os.tmpdir(), rndName()); + const tmpFile = join(os.tmpdir(), rndName() + '.' + fileExtension); fs.writeFile(tmpFile, contents, (error) => { if (error) { return reject(error); @@ -53,8 +53,8 @@ export function closeAllEditors(): Thenable { } -export function withRandomFileEditor(initialContents: string, run: (editor: vscode.TextEditor, doc: vscode.TextDocument) => Thenable): Thenable { - return createRandomFile(initialContents).then(file => { +export function withRandomFileEditor(initialContents: string, fileExtension: string = 'txt', run: (editor: vscode.TextEditor, doc: vscode.TextDocument) => Thenable): Thenable { + return createRandomFile(initialContents, fileExtension).then(file => { return vscode.workspace.openTextDocument(file).then(doc => { return vscode.window.showTextDocument(doc).then((editor) => { return run(editor, doc).then(_ => { From 76392ae91fc140bf626c0d9c20597ca58b4784ae Mon Sep 17 00:00:00 2001 From: Ramya Achutha Rao Date: Mon, 24 Jul 2017 14:42:24 -0700 Subject: [PATCH 021/136] Emmet Reflect CSS Value in html files --- extensions/emmet/src/reflectCssValue.ts | 15 +++------ .../emmet/src/test/reflectCssValue.test.ts | 32 ++++++++++++++++--- extensions/emmet/src/util.ts | 26 +++++++++++++++ 3 files changed, 58 insertions(+), 15 deletions(-) diff --git a/extensions/emmet/src/reflectCssValue.ts b/extensions/emmet/src/reflectCssValue.ts index 4aa1319df6e..2e07c39cc8b 100644 --- a/extensions/emmet/src/reflectCssValue.ts +++ b/extensions/emmet/src/reflectCssValue.ts @@ -4,8 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { Range, window, TextEditor } from 'vscode'; -import { isStyleSheet } from 'vscode-emmet-helper'; -import { parse, getNode, getCssProperty } from './util'; +import { getCssProperty, getCssPropertyNode } from './util'; import { Property, Rule } from 'EmmetNode'; const vendorPrefixes = ['-webkit-', '-moz-', '-ms-', '-o-', '']; @@ -17,18 +16,12 @@ export function reflectCssValue() { return; } - if (!isStyleSheet(editor.document.languageId)) { + let node = getCssPropertyNode(editor, editor.selection.active); + if (!node) { return; } - const rootNode = parse(editor.document); - const node = getNode(rootNode, editor.selection.active, true); - if (!node || node.type !== 'property') { - return; - } - - return updateCSSNode(editor, node); - + return updateCSSNode(editor, node); } function updateCSSNode(editor: TextEditor, property: Property) { diff --git a/extensions/emmet/src/test/reflectCssValue.test.ts b/extensions/emmet/src/test/reflectCssValue.test.ts index fdc7f8b422a..a3601117706 100644 --- a/extensions/emmet/src/test/reflectCssValue.test.ts +++ b/extensions/emmet/src/test/reflectCssValue.test.ts @@ -10,7 +10,7 @@ import { withRandomFileEditor, closeAllEditors } from './testUtils'; suite('Tests for Emmet: Reflect CSS Value command', () => { teardown(closeAllEditors); - const contents = ` + const cssContents = ` .header { margin: 10px; padding: 10px; @@ -22,16 +22,40 @@ suite('Tests for Emmet: Reflect CSS Value command', () => { } `; - test('reflectCssValue', function (): any { + const htmlContents = ` + + + + `; - return withRandomFileEditor(contents, '.css', (editor, doc) => { + test('Reflect Css Value in css file', function (): any { + return withRandomFileEditor(cssContents, '.css', (editor, doc) => { editor.selections = [new Selection(5, 10, 5, 10)]; return commands.executeCommand('emmet.reflectCssValue').then(() => { - assert.equal(doc.getText(), contents.replace(/\(50deg\)/g, '(20deg)')); + assert.equal(doc.getText(), cssContents.replace(/\(50deg\)/g, '(20deg)')); return Promise.resolve(); }); }); }); + test('Reflect Css Value in html file', function (): any { + return withRandomFileEditor(htmlContents, '.html', (editor, doc) => { + editor.selections = [new Selection(7, 20, 7, 20)]; + return commands.executeCommand('emmet.reflectCssValue').then(() => { + assert.equal(doc.getText(), htmlContents.replace(/\(50deg\)/g, '(20deg)')); + return Promise.resolve(); + }); + }); + }); }); \ No newline at end of file diff --git a/extensions/emmet/src/util.ts b/extensions/emmet/src/util.ts index a0b8900d385..df165135694 100644 --- a/extensions/emmet/src/util.ts +++ b/extensions/emmet/src/util.ts @@ -286,3 +286,29 @@ export function iterateCSSToken(token: CssToken, fn) { export function getCssProperty(rule, name): Property { return rule.children.find(node => node.type === 'property' && node.name === name); } + +/** + * Returns css property under caret in given editor or `null` if such node cannot + * be found + * @param {TextEditor} editor + * @return {Property} + */ +export function getCssPropertyNode(editor: vscode.TextEditor, position: vscode.Position): Property { + const rootNode = this.parse(editor.document); + const node = getNode(rootNode, position); + + if (isStyleSheet(editor.document.languageId)) { + return node && node.type === 'property' ? node : null; + } + + let htmlNode = node; + if (htmlNode + && htmlNode.name === 'style' + && htmlNode.open.end.isBefore(position) + && htmlNode.close.start.isAfter(position)) { + let buffer = new DocumentStreamReader(editor.document, htmlNode.start, new vscode.Range(htmlNode.start, htmlNode.end)); + let rootNode = parseStylesheet(buffer); + const node = getNode(rootNode, position); + return (node && node.type === 'property') ? node : null; + } +} From bf634bf4e77683237944ac498e9bcd0057c77a2e Mon Sep 17 00:00:00 2001 From: Ramya Achutha Rao Date: Mon, 24 Jul 2017 14:45:02 -0700 Subject: [PATCH 022/136] Renaming utilties for clarity --- extensions/emmet/src/abbreviationActions.ts | 4 ++-- extensions/emmet/src/balance.ts | 4 ++-- extensions/emmet/src/defaultCompletionProvider.ts | 4 ++-- extensions/emmet/src/matchTag.ts | 4 ++-- extensions/emmet/src/mergeLines.ts | 4 ++-- extensions/emmet/src/reflectCssValue.ts | 6 +++--- extensions/emmet/src/removeTag.ts | 4 ++-- extensions/emmet/src/selectItem.ts | 4 ++-- extensions/emmet/src/splitJoinTag.ts | 4 ++-- extensions/emmet/src/toggleComment.ts | 4 ++-- extensions/emmet/src/updateImageSize.ts | 12 ++++++------ extensions/emmet/src/updateTag.ts | 4 ++-- extensions/emmet/src/util.ts | 8 ++++---- 13 files changed, 33 insertions(+), 33 deletions(-) diff --git a/extensions/emmet/src/abbreviationActions.ts b/extensions/emmet/src/abbreviationActions.ts index fc2958ccd4d..43b0a46f16a 100644 --- a/extensions/emmet/src/abbreviationActions.ts +++ b/extensions/emmet/src/abbreviationActions.ts @@ -6,7 +6,7 @@ import * as vscode from 'vscode'; import { expand } from '@emmetio/expand-abbreviation'; import { Node, HtmlNode, Rule } from 'EmmetNode'; -import { getNode, getInnerRange, getMappingForIncludedLanguages, parse, validate } from './util'; +import { getNode, getInnerRange, getMappingForIncludedLanguages, parseDocument, validate } from './util'; import { getExpandOptions, extractAbbreviation, isStyleSheet, isAbbreviationValid, getEmmetMode } from 'vscode-emmet-helper'; interface ExpandAbbreviationInput { @@ -88,7 +88,7 @@ export function expandAbbreviation(args) { const editor = vscode.window.activeTextEditor; - let rootNode = parse(editor.document); + let rootNode = parseDocument(editor.document); if (!rootNode) { return; } diff --git a/extensions/emmet/src/balance.ts b/extensions/emmet/src/balance.ts index 164b4cb0c37..f3fb4e47984 100644 --- a/extensions/emmet/src/balance.ts +++ b/extensions/emmet/src/balance.ts @@ -5,7 +5,7 @@ import * as vscode from 'vscode'; import { HtmlNode } from 'EmmetNode'; -import { getNode, parse, validate } from './util'; +import { getNode, parseDocument, validate } from './util'; export function balanceOut() { balance(true); @@ -21,7 +21,7 @@ function balance(out: boolean) { return; } - let rootNode = parse(editor.document); + let rootNode = parseDocument(editor.document); if (!rootNode) { return; } diff --git a/extensions/emmet/src/defaultCompletionProvider.ts b/extensions/emmet/src/defaultCompletionProvider.ts index 1e35e738cf2..e959d4730b1 100644 --- a/extensions/emmet/src/defaultCompletionProvider.ts +++ b/extensions/emmet/src/defaultCompletionProvider.ts @@ -7,7 +7,7 @@ import * as vscode from 'vscode'; import { HtmlNode } from 'EmmetNode'; import { doComplete, isStyleSheet, getEmmetMode } from 'vscode-emmet-helper'; import { isValidLocationForEmmetAbbreviation } from './abbreviationActions'; -import { getNode, getInnerRange, getMappingForIncludedLanguages, parse, getEmmetConfiguration } from './util'; +import { getNode, getInnerRange, getMappingForIncludedLanguages, parseDocument, getEmmetConfiguration } from './util'; export class DefaultCompletionItemProvider implements vscode.CompletionItemProvider { @@ -61,7 +61,7 @@ export class DefaultCompletionItemProvider implements vscode.CompletionItemProvi if (!syntax) { return syntax; } - let rootNode = parse(document, false); + let rootNode = parseDocument(document, false); if (!rootNode) { return; } diff --git a/extensions/emmet/src/matchTag.ts b/extensions/emmet/src/matchTag.ts index 52222a157c9..7510346b0a9 100644 --- a/extensions/emmet/src/matchTag.ts +++ b/extensions/emmet/src/matchTag.ts @@ -5,7 +5,7 @@ import * as vscode from 'vscode'; import { HtmlNode } from 'EmmetNode'; -import { getNode, parse, validate } from './util'; +import { getNode, parseDocument, validate } from './util'; export function matchTag() { let editor = vscode.window.activeTextEditor; @@ -13,7 +13,7 @@ export function matchTag() { return; } - let rootNode = parse(editor.document); + let rootNode = parseDocument(editor.document); if (!rootNode) { return; } diff --git a/extensions/emmet/src/mergeLines.ts b/extensions/emmet/src/mergeLines.ts index 927db50d971..430ed14c3b5 100644 --- a/extensions/emmet/src/mergeLines.ts +++ b/extensions/emmet/src/mergeLines.ts @@ -5,7 +5,7 @@ import * as vscode from 'vscode'; import { Node } from 'EmmetNode'; -import { getNode, parse, validate } from './util'; +import { getNode, parseDocument, validate } from './util'; export function mergeLines() { let editor = vscode.window.activeTextEditor; @@ -13,7 +13,7 @@ export function mergeLines() { return; } - let rootNode = parse(editor.document); + let rootNode = parseDocument(editor.document); if (!rootNode) { return; } diff --git a/extensions/emmet/src/reflectCssValue.ts b/extensions/emmet/src/reflectCssValue.ts index 2e07c39cc8b..7e03bb8f896 100644 --- a/extensions/emmet/src/reflectCssValue.ts +++ b/extensions/emmet/src/reflectCssValue.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { Range, window, TextEditor } from 'vscode'; -import { getCssProperty, getCssPropertyNode } from './util'; +import { getCssPropertyFromRule, getCssPropertyFromDocument } from './util'; import { Property, Rule } from 'EmmetNode'; const vendorPrefixes = ['-webkit-', '-moz-', '-ms-', '-o-', '']; @@ -16,7 +16,7 @@ export function reflectCssValue() { return; } - let node = getCssPropertyNode(editor, editor.selection.active); + let node = getCssPropertyFromDocument(editor, editor.selection.active); if (!node) { return; } @@ -45,7 +45,7 @@ function updateCSSNode(editor: TextEditor, property: Property) { if (prefix === currentPrefix) { return; } - let vendorProperty = getCssProperty(rule, prefix + propertyName); + let vendorProperty = getCssPropertyFromRule(rule, prefix + propertyName); if (vendorProperty) { builder.replace(new Range(vendorProperty.valueToken.start, vendorProperty.valueToken.end), propertyValue); } diff --git a/extensions/emmet/src/removeTag.ts b/extensions/emmet/src/removeTag.ts index 4594db42022..a7f68098457 100644 --- a/extensions/emmet/src/removeTag.ts +++ b/extensions/emmet/src/removeTag.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import * as vscode from 'vscode'; -import { parse, validate, getNode } from './util'; +import { parseDocument, validate, getNode } from './util'; import { HtmlNode } from 'EmmetNode'; export function removeTag() { @@ -13,7 +13,7 @@ export function removeTag() { return; } - let rootNode = parse(editor.document); + let rootNode = parseDocument(editor.document); if (!rootNode) { return; } diff --git a/extensions/emmet/src/selectItem.ts b/extensions/emmet/src/selectItem.ts index d15675d59a4..25b918231bd 100644 --- a/extensions/emmet/src/selectItem.ts +++ b/extensions/emmet/src/selectItem.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import * as vscode from 'vscode'; -import { validate, parse } from './util'; +import { validate, parseDocument } from './util'; import { nextItemHTML, prevItemHTML } from './selectItemHTML'; import { nextItemStylesheet, prevItemStylesheet } from './selectItemStylesheet'; import { isStyleSheet } from 'vscode-emmet-helper'; @@ -27,7 +27,7 @@ export function fetchSelectItem(direction: string): void { prevItem = prevItemHTML; } - let rootNode = parse(editor.document); + let rootNode = parseDocument(editor.document); if (!rootNode) { return; } diff --git a/extensions/emmet/src/splitJoinTag.ts b/extensions/emmet/src/splitJoinTag.ts index cdbed5a572e..56b24bf9c4a 100644 --- a/extensions/emmet/src/splitJoinTag.ts +++ b/extensions/emmet/src/splitJoinTag.ts @@ -5,7 +5,7 @@ import * as vscode from 'vscode'; import Node from '@emmetio/node'; -import { getNode, parse, validate } from './util'; +import { getNode, parseDocument, validate } from './util'; export function splitJoinTag() { let editor = vscode.window.activeTextEditor; @@ -13,7 +13,7 @@ export function splitJoinTag() { return; } - let rootNode = parse(editor.document); + let rootNode = parseDocument(editor.document); if (!rootNode) { return; } diff --git a/extensions/emmet/src/toggleComment.ts b/extensions/emmet/src/toggleComment.ts index 8f9200b96c5..48a3c872e55 100644 --- a/extensions/emmet/src/toggleComment.ts +++ b/extensions/emmet/src/toggleComment.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import * as vscode from 'vscode'; -import { getNodesInBetween, getNode, parse } from './util'; +import { getNodesInBetween, getNode, parseDocument } from './util'; import { Node, Stylesheet } from 'EmmetNode'; import { isStyleSheet } from 'vscode-emmet-helper'; @@ -34,7 +34,7 @@ export function toggleComment() { endComment = endCommentHTML; } - let rootNode = parse(editor.document); + let rootNode = parseDocument(editor.document); if (!rootNode) { return; } diff --git a/extensions/emmet/src/updateImageSize.ts b/extensions/emmet/src/updateImageSize.ts index 713b9c1f8c3..46edeee044d 100644 --- a/extensions/emmet/src/updateImageSize.ts +++ b/extensions/emmet/src/updateImageSize.ts @@ -11,7 +11,7 @@ import { TextEditor, Range, Position, window } from 'vscode'; import * as path from 'path'; import { getImageSize } from './imageSizeHelper'; import { isStyleSheet } from 'vscode-emmet-helper'; -import { parse, getNode, iterateCSSToken, getCssProperty } from './util'; +import { parseDocument, getNode, iterateCSSToken, getCssPropertyFromRule } from './util'; import { HtmlNode, CssToken, HtmlToken, Attribute, Property } from 'EmmetNode'; import { locateFile } from './locateFile'; import parseStylesheet from '@emmetio/css-parser'; @@ -59,7 +59,7 @@ function updateImageSizeHTML(editor: TextEditor) { function updateImageSizeStyleTag(editor: TextEditor) { let getPropertyInsiderStyleTag = (editor) => { - const rootNode = parse(editor.document); + const rootNode = parseDocument(editor.document); const currentNode = getNode(rootNode, editor.selection.active); if (currentNode && currentNode.name === 'style' && currentNode.open.end.isBefore(editor.selection.active) @@ -109,7 +109,7 @@ function updateImageSizeCSS(editor: TextEditor, fetchNode: (editor) => Property) * @return {HtmlNode} */ function getImageHTMLNode(editor: TextEditor): HtmlNode { - const rootNode = parse(editor.document); + const rootNode = parseDocument(editor.document); const node = getNode(rootNode, editor.selection.active, true); return node && node.name.toLowerCase() === 'img' ? node : null; @@ -122,7 +122,7 @@ function getImageHTMLNode(editor: TextEditor): HtmlNode { * @return {Property} */ function getImageCSSNode(editor: TextEditor): Property { - const rootNode = parse(editor.document); + const rootNode = parseDocument(editor.document); const node = getNode(rootNode, editor.selection.active, true); return node && node.type === 'property' ? node : null; } @@ -213,8 +213,8 @@ function updateHTMLTag(editor: TextEditor, node: HtmlNode, width: number, height */ function updateCSSNode(editor: TextEditor, srcProp: Property, width: number, height: number) { const rule = srcProp.parent; - const widthProp = getCssProperty(rule, 'width'); - const heightProp = getCssProperty(rule, 'height'); + const widthProp = getCssPropertyFromRule(rule, 'width'); + const heightProp = getCssPropertyFromRule(rule, 'height'); // Detect formatting const separator = srcProp.separator || ': '; diff --git a/extensions/emmet/src/updateTag.ts b/extensions/emmet/src/updateTag.ts index 204c31741a4..66a23e074b6 100644 --- a/extensions/emmet/src/updateTag.ts +++ b/extensions/emmet/src/updateTag.ts @@ -5,14 +5,14 @@ import * as vscode from 'vscode'; import { HtmlNode } from 'EmmetNode'; -import { getNode, parse, validate } from './util'; +import { getNode, parseDocument, validate } from './util'; export function updateTag(tagName: string): Thenable { let editor = vscode.window.activeTextEditor; if (!validate(false)) { return; } - let rootNode = parse(editor.document); + let rootNode = parseDocument(editor.document); if (!rootNode) { return; } diff --git a/extensions/emmet/src/util.ts b/extensions/emmet/src/util.ts index df165135694..23eb6ec4484 100644 --- a/extensions/emmet/src/util.ts +++ b/extensions/emmet/src/util.ts @@ -63,7 +63,7 @@ export function getMappingForIncludedLanguages(): any { * Parses the given document using emmet parsing modules * @param document */ -export function parse(document: vscode.TextDocument, showError: boolean = true): Node { +export function parseDocument(document: vscode.TextDocument, showError: boolean = true): Node { let parseContent = isStyleSheet(document.languageId) ? parseStylesheet : parse; let rootNode: Node; try { @@ -283,7 +283,7 @@ export function iterateCSSToken(token: CssToken, fn) { * @param {String} name * @return {Property} */ -export function getCssProperty(rule, name): Property { +export function getCssPropertyFromRule(rule, name): Property { return rule.children.find(node => node.type === 'property' && node.name === name); } @@ -293,8 +293,8 @@ export function getCssProperty(rule, name): Property { * @param {TextEditor} editor * @return {Property} */ -export function getCssPropertyNode(editor: vscode.TextEditor, position: vscode.Position): Property { - const rootNode = this.parse(editor.document); +export function getCssPropertyFromDocument(editor: vscode.TextEditor, position: vscode.Position): Property { + const rootNode = parseDocument(editor.document); const node = getNode(rootNode, position); if (isStyleSheet(editor.document.languageId)) { From 7a0f65bea190077b713ab9f991da8345eaf8c202 Mon Sep 17 00:00:00 2001 From: Ramya Achutha Rao Date: Mon, 24 Jul 2017 15:59:11 -0700 Subject: [PATCH 023/136] Tests for Remove, Split, Join and Match tags --- extensions/emmet/src/extension.ts | 4 +- extensions/emmet/src/removeTag.ts | 5 +- extensions/emmet/src/splitJoinTag.ts | 2 +- extensions/emmet/src/test/tagActions.test.ts | 123 +++++++++++++++++++ extensions/emmet/src/test/updateTag.test.ts | 46 ------- 5 files changed, 127 insertions(+), 53 deletions(-) create mode 100644 extensions/emmet/src/test/tagActions.test.ts delete mode 100644 extensions/emmet/src/test/updateTag.test.ts diff --git a/extensions/emmet/src/extension.ts b/extensions/emmet/src/extension.ts index 45118cb5cfb..62b096ba1b0 100644 --- a/extensions/emmet/src/extension.ts +++ b/extensions/emmet/src/extension.ts @@ -36,7 +36,7 @@ export function activate(context: vscode.ExtensionContext) { })); context.subscriptions.push(vscode.commands.registerCommand('emmet.removeTag', () => { - removeTag(); + return removeTag(); })); context.subscriptions.push(vscode.commands.registerCommand('emmet.updateTag', (inputTag) => { @@ -61,7 +61,7 @@ export function activate(context: vscode.ExtensionContext) { })); context.subscriptions.push(vscode.commands.registerCommand('emmet.splitJoinTag', () => { - splitJoinTag(); + return splitJoinTag(); })); context.subscriptions.push(vscode.commands.registerCommand('emmet.mergeLines', () => { diff --git a/extensions/emmet/src/removeTag.ts b/extensions/emmet/src/removeTag.ts index a7f68098457..db98df8cf62 100644 --- a/extensions/emmet/src/removeTag.ts +++ b/extensions/emmet/src/removeTag.ts @@ -28,7 +28,7 @@ export function removeTag() { rangesToRemove = rangesToRemove.concat(getRangeToRemove(editor, rootNode, selection, indentInSpaces)); }); - editor.edit(editBuilder => { + return editor.edit(editBuilder => { rangesToRemove.forEach(range => { editBuilder.replace(range, ''); }); @@ -48,9 +48,6 @@ function getRangeToRemove(editor: vscode.TextEditor, rootNode: HtmlNode, selecti closeRange = new vscode.Range(nodeToUpdate.close.start, nodeToUpdate.close.end); } - if (!openRange.contains(selection.start) && !closeRange.contains(selection.start)) { - return []; - } let ranges = [openRange]; if (closeRange) { for (let i = openRange.start.line + 1; i <= closeRange.start.line; i++) { diff --git a/extensions/emmet/src/splitJoinTag.ts b/extensions/emmet/src/splitJoinTag.ts index 56b24bf9c4a..619f06af314 100644 --- a/extensions/emmet/src/splitJoinTag.ts +++ b/extensions/emmet/src/splitJoinTag.ts @@ -18,7 +18,7 @@ export function splitJoinTag() { return; } - editor.edit(editBuilder => { + return editor.edit(editBuilder => { editor.selections.reverse().forEach(selection => { let [rangeToReplace, textToReplaceWith] = getRangesToReplace(editor.document, selection, rootNode); if (rangeToReplace && textToReplaceWith) { diff --git a/extensions/emmet/src/test/tagActions.test.ts b/extensions/emmet/src/test/tagActions.test.ts new file mode 100644 index 00000000000..274098c3fd5 --- /dev/null +++ b/extensions/emmet/src/test/tagActions.test.ts @@ -0,0 +1,123 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as assert from 'assert'; +import { Selection, commands } from 'vscode'; +import { withRandomFileEditor, closeAllEditors } from './testUtils'; + +suite('Tests for Emmet actions on html tags', () => { + teardown(closeAllEditors); + + const contents = ` +
+
    +
  • Hello
  • +
  • There
  • +
  • Bye
  • +
+ +
+ `; + + test('update tag with multiple cursors', () => { + const expectedContents = ` +
+
    +
  • Hello
  • +
    There
    +
  • Bye
  • +
+ +
+ `; + return withRandomFileEditor(contents, 'html', (editor, doc) => { + editor.selections = [ + new Selection(3, 17, 3, 17), // cursor inside tags + new Selection(4, 5, 4, 5), // cursor inside opening tag + new Selection(5, 35, 5, 35), // cursor inside closing tag + ]; + + return commands.executeCommand('emmet.updateTag', 'section').then(() => { + assert.equal(doc.getText(), expectedContents); + return Promise.resolve(); + }); + }); + }); + + + test('remove tag with mutliple cursors', () => { + const expectedContents = ` +
+
    +
  • Hello
  • + There +
  • Bye
  • +
+ +
+ `; + return withRandomFileEditor(contents, 'html', (editor, doc) => { + editor.selections = [ + new Selection(3, 17, 3, 17), // cursor inside tags + new Selection(4, 5, 4, 5), // cursor inside opening tag + new Selection(5, 35, 5, 35), // cursor inside closing tag + ]; + + return commands.executeCommand('emmet.removeTag').then(() => { + assert.equal(doc.getText(), expectedContents); + return Promise.resolve(); + }); + }); + }); + + test('split/join tag with mutliple cursors', () => { + const expectedContents = ` +
+
    +
  • +
  • There
  • +
  • Bye
  • +
+ +
+ `; + return withRandomFileEditor(contents, 'html', (editor, doc) => { + editor.selections = [ + new Selection(3, 17, 3, 17), // join tag + new Selection(7, 5, 7, 5), // split tag + ]; + + return commands.executeCommand('emmet.splitJoinTag').then(() => { + assert.equal(doc.getText(), expectedContents); + return Promise.resolve(); + }); + }); + }); + + test('match tag with mutliple cursors', () => { + return withRandomFileEditor(contents, 'html', (editor, doc) => { + editor.selections = [ + new Selection(1, 0, 1, 0), // just before tag starts, i.e before < + new Selection(1, 1, 1, 1), // just before tag name starts + new Selection(1, 2, 1, 2), // inside tag name + new Selection(1, 6, 1, 6), // after tag name but before opening tag ends + new Selection(1, 18, 1, 18), // just before opening tag ends + new Selection(1, 19, 1, 19), // just after opening tag ends + ]; + + return commands.executeCommand('emmet.matchTag').then(() => { + editor.selections.forEach(selection => { + assert.equal(selection.active.line, 8); + assert.equal(selection.active.character, 3); + assert.equal(selection.anchor.line, 8); + assert.equal(selection.anchor.character, 3); + }); + + return Promise.resolve(); + }); + }); + }); + +}); \ No newline at end of file diff --git a/extensions/emmet/src/test/updateTag.test.ts b/extensions/emmet/src/test/updateTag.test.ts deleted file mode 100644 index 58f72a439ea..00000000000 --- a/extensions/emmet/src/test/updateTag.test.ts +++ /dev/null @@ -1,46 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import * as assert from 'assert'; -import { Selection, commands } from 'vscode'; -import { withRandomFileEditor, closeAllEditors } from './testUtils'; - -suite('Tests for Emmet: Update Tag', () => { - teardown(closeAllEditors); - - const contents = ` -
-
    -
  • Hello
  • -
  • There
  • -
  • Bye
  • -
-
- `; - - /* test('update tag with multiple cursors', () => { - const expectedContents = ` -
-
    -
  • Hello
  • -
    There
    -
  • Bye
  • -
-
- `; - return withRandomFileEditor(contents, (editor, doc) => { - editor.selections = [ - new Selection(3, 17, 3, 17), // cursor inside tags - new Selection(4, 14, 4, 14), // cursor inside opening tag - new Selection(5, 47, 5, 47), // cursor inside closing tag - ]; - - return commands.executeCommand('emmet.updateTag', 'section').then(() => { - assert.equal(doc.getText(), expectedContents); - return Promise.resolve(); - }); - }); - }); */ -}); \ No newline at end of file From 6f36994285c7d4ba12a5c4f601e222535da7396f Mon Sep 17 00:00:00 2001 From: Ramya Achutha Rao Date: Mon, 24 Jul 2017 16:10:09 -0700 Subject: [PATCH 024/136] Absorb upstream emmet snippet changes Fixes #30496 --- extensions/emmet/npm-shrinkwrap.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/extensions/emmet/npm-shrinkwrap.json b/extensions/emmet/npm-shrinkwrap.json index 706f04d0359..3a293f0d3f5 100644 --- a/extensions/emmet/npm-shrinkwrap.json +++ b/extensions/emmet/npm-shrinkwrap.json @@ -88,9 +88,9 @@ "resolved": "https://registry.npmjs.org/@emmetio/output-renderer/-/output-renderer-0.1.1.tgz" }, "@emmetio/snippets": { - "version": "0.2.3", + "version": "0.2.4", "from": "@emmetio/snippets@>=0.2.3 <0.3.0", - "resolved": "https://registry.npmjs.org/@emmetio/snippets/-/snippets-0.2.3.tgz" + "resolved": "https://registry.npmjs.org/@emmetio/snippets/-/snippets-0.2.4.tgz" }, "@emmetio/snippets-registry": { "version": "0.3.1", From 8201d4606b3eff92505898528d8e19b77d52633c Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Wed, 19 Jul 2017 22:07:14 -0700 Subject: [PATCH 025/136] Multi-root querybuilder tests --- .../parts/search/common/queryBuilder.ts | 6 +- .../search/test/common/queryBuilder.test.ts | 303 +++++++++++++----- 2 files changed, 224 insertions(+), 85 deletions(-) diff --git a/src/vs/workbench/parts/search/common/queryBuilder.ts b/src/vs/workbench/parts/search/common/queryBuilder.ts index a08bbb38d59..4d9052311cd 100644 --- a/src/vs/workbench/parts/search/common/queryBuilder.ts +++ b/src/vs/workbench/parts/search/common/queryBuilder.ts @@ -165,7 +165,11 @@ export class QueryBuilder { const searchPathRoot = relativeSearchPathMatch[1]; const matchingRoots = workspace.roots.filter(root => paths.basename(root.fsPath) === searchPathRoot); if (matchingRoots.length) { - return matchingRoots.map(root => paths.join(root.fsPath, relativeSearchPathMatch[2] || '')); + return matchingRoots.map(root => { + return relativeSearchPathMatch[2] ? + paths.join(root.fsPath, relativeSearchPathMatch[2]) : + root.fsPath; + }); } else { // throw new Error(nls.localize('search.invalidRootFolder', 'No root folder named {}', searchPathRoot)); } diff --git a/src/vs/workbench/parts/search/test/common/queryBuilder.test.ts b/src/vs/workbench/parts/search/test/common/queryBuilder.test.ts index 62fc4e9bef5..3d860194a25 100644 --- a/src/vs/workbench/parts/search/test/common/queryBuilder.test.ts +++ b/src/vs/workbench/parts/search/test/common/queryBuilder.test.ts @@ -9,29 +9,34 @@ import { IExpression } from 'vs/base/common/glob'; import uri from 'vs/base/common/uri'; import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; +import { IWorkspaceContextService, Workspace } from 'vs/platform/workspace/common/workspace'; import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService'; import { QueryBuilder, ISearchPathsResult } from 'vs/workbench/parts/search/common/queryBuilder'; +import { TestContextService } from 'vs/workbench/test/workbenchTestServices'; import { ISearchQuery, QueryType, IPatternInfo } from 'vs/platform/search/common/search'; suite('SearchQuery', () => { const PATTERN_INFO: IPatternInfo = { pattern: 'a' }; - const PROJECT_URI = uri.parse('/foo/bar'); + const ROOT_1 = `/foo/root1`; + const ROOT_1_URI = uri.parse(ROOT_1); let instantiationService: TestInstantiationService; let queryBuilder: QueryBuilder; let mockConfigService: TestConfigurationService; + let mockContextService: TestContextService; + let mockWorkspace: Workspace; setup(() => { instantiationService = new TestInstantiationService(); + mockConfigService = new TestConfigurationService(); instantiationService.stub(IConfigurationService, mockConfigService); - instantiationService.stub(IWorkspaceContextService, { - hasWorkspace: () => { - return true; - }, - }); + + mockContextService = new TestContextService(); + mockWorkspace = new Workspace('workspace', 'workspace', [ROOT_1_URI]); + mockContextService.setWorkspace(mockWorkspace); + instantiationService.stub(IWorkspaceContextService, mockContextService); queryBuilder = instantiationService.createInstance(QueryBuilder); }); @@ -51,37 +56,37 @@ suite('SearchQuery', () => { assertEqualQueries( queryBuilder.text( PATTERN_INFO, - [PROJECT_URI] + [ROOT_1_URI] ), { contentPattern: PATTERN_INFO, - folderQueries: [{ folder: PROJECT_URI }], + folderQueries: [{ folder: ROOT_1_URI }], type: QueryType.Text, useRipgrep: true }); }); - test('simple includes', () => { - function testSimpleIncludes(includePattern: string, expectedPatterns: string[]): void { - assert.deepEqual( - queryBuilder.parseSearchPaths(includePattern), - { - includePattern: patternsToIExpression(...expectedPatterns.map(globalGlob)) - }, - includePattern); - } + suite('parseSearchPaths', () => { + test('simple includes', () => { + function testSimpleIncludes(includePattern: string, expectedPatterns: string[]): void { + assert.deepEqual( + queryBuilder.parseSearchPaths(includePattern), + { + includePattern: patternsToIExpression(...expectedPatterns.map(globalGlob)) + }, + includePattern); + } - [ - ['a', ['a']], - ['a/b', ['a/b']], - ['a/b, c', ['a/b', 'c']], - ['a,.txt', ['a', '*.txt']], - ['a,,,b', ['a', 'b']], - ['**/a,b/**', ['**/a', 'b/**']] - ].forEach(([includePattern, expectedPatterns]) => testSimpleIncludes(includePattern, expectedPatterns)); - }); + [ + ['a', ['a']], + ['a/b', ['a/b']], + ['a/b, c', ['a/b', 'c']], + ['a,.txt', ['a', '*.txt']], + ['a,,,b', ['a', 'b']], + ['**/a,b/**', ['**/a', 'b/**']] + ].forEach(([includePattern, expectedPatterns]) => testSimpleIncludes(includePattern, expectedPatterns)); + }); - test('absolute includes', () => { function testIncludes(includePattern: string, expectedResult: ISearchPathsResult): void { assertEqualSearchPathResults( queryBuilder.parseSearchPaths(includePattern), @@ -89,63 +94,193 @@ suite('SearchQuery', () => { includePattern); } - [ + function testIncludesDataItem([includePattern, expectedResult]): void { + testIncludes(includePattern, expectedResult); + } + + test('absolute includes', () => { [ - '/foo/bar', - { - searchPaths: [{ searchPath: uri.parse('/foo/bar') }] - } - ], + [ + '/foo/bar', + { + searchPaths: [{ searchPath: uri.parse('/foo/bar') }] + } + ], + [ + '/foo/bar,a', + { + searchPaths: [{ searchPath: uri.parse('/foo/bar') }], + includePattern: patternsToIExpression(globalGlob('a')) + } + ], + [ + '/foo/bar, /1/2', + { + searchPaths: [{ searchPath: uri.parse('/foo/bar') }, { searchPath: uri.parse('/1/2') }] + } + ], + [ + '/foo/bar/**/*.ts', + { + searchPaths: [{ + searchPath: uri.parse('/foo/bar'), + pattern: '**/*.ts' + }] + } + ], + [ + '/foo/bar/*a/b/c', + { + searchPaths: [{ + searchPath: uri.parse('/foo/bar'), + pattern: '*a/b/c' + }] + } + ], + [ + '/*a/b/c', + { + searchPaths: [{ + searchPath: uri.parse('/'), + pattern: '*a/b/c' + }] + } + ], + [ + '/foo/{b,c}ar', + { + searchPaths: [{ + searchPath: uri.parse('/foo'), + pattern: '{b,c}ar' + }] + } + ] + ].forEach(testIncludesDataItem); + }); + + test('relative includes w/single root folder', () => { [ - '/foo/bar,a', - { - searchPaths: [{ searchPath: uri.parse('/foo/bar') }], - includePattern: patternsToIExpression(globalGlob('a')) - } - ], + [ + './a', + { + searchPaths: [{ + searchPath: uri.parse(ROOT_1 + '/a') + }] + } + ], + [ + './a/*b/c', + { + searchPaths: [{ + searchPath: uri.parse(ROOT_1 + '/a'), + pattern: '*b/c' + }] + } + ], + [ + './a/*b/c, /project/foo', + { + searchPaths: [ + { + searchPath: uri.parse(ROOT_1 + '/a'), + pattern: '*b/c' + }, + { + searchPath: uri.parse('/project/foo') + }] + } + ] + ].forEach(testIncludesDataItem); + }); + + test('relative includes w/two root folders', () => { + const ROOT_2 = '/project/root2'; + mockWorkspace.roots = [ROOT_1_URI, uri.parse(ROOT_2)]; + [ - '/foo/bar, /1/2', - { - searchPaths: [{ searchPath: uri.parse('/foo/bar') }, { searchPath: uri.parse('/1/2') }] - } - ], + [ + './root1', + { + searchPaths: [{ + searchPath: uri.parse(ROOT_1) + }] + } + ], + [ + './root2', + { + searchPaths: [{ + searchPath: uri.parse(ROOT_2), + }] + } + ], + [ + './root1/a/**/b, ./root2/**/*.txt', + { + searchPaths: [ + { + searchPath: uri.parse(ROOT_1 + '/a'), + pattern: '**/b' + }, + { + searchPath: uri.parse(ROOT_2), + pattern: '**/*.txt' + }] + } + ] + ].forEach(testIncludesDataItem); + }); + + test('relative includes w/multiple ambiguous root folders', () => { + const ROOT_2 = '/project/rootB'; + const ROOT_3 = '/otherproject/rootB'; + mockWorkspace.roots = [ROOT_1_URI, uri.parse(ROOT_2), uri.parse(ROOT_3)]; + [ - '/foo/bar/**/*.ts', - { - searchPaths: [{ - searchPath: uri.parse('/foo/bar'), - pattern: '**/*.ts' - }] - } - ], - [ - '/foo/bar/*a/b/c', - { - searchPaths: [{ - searchPath: uri.parse('/foo/bar'), - pattern: '*a/b/c' - }] - } - ], - [ - '/*a/b/c', - { - searchPaths: [{ - searchPath: uri.parse('/'), - pattern: '*a/b/c' - }] - } - ], - [ - '/foo/{b,c}ar', - { - searchPaths: [{ - searchPath: uri.parse('/foo'), - pattern: '{b,c}ar' - }] - } - ] - ].forEach(([includePattern, expectedResult]) => testIncludes(includePattern, expectedResult)); + [ + './root1', + { + searchPaths: [{ + searchPath: uri.parse(ROOT_1) + }] + } + ], + [ + './rootB', + { + searchPaths: [ + { + searchPath: uri.parse(ROOT_2), + }, + { + searchPath: uri.parse(ROOT_3), + }] + } + ], + [ + './rootB/a/**/b, ./rootB/b/**/*.txt', + { + searchPaths: [ + { + searchPath: uri.parse(ROOT_2 + '/a'), + pattern: '**/b' + }, + { + searchPath: uri.parse(ROOT_3 + '/a'), + pattern: '**/b' + }, + { + searchPath: uri.parse(ROOT_2 + '/b'), + pattern: '**/*.txt' + }, + { + searchPath: uri.parse(ROOT_3 + '/b'), + pattern: '**/*.txt' + }] + } + ] + ].forEach(testIncludesDataItem); + }); }); }); @@ -158,11 +293,11 @@ function assertEqualSearchPathResults(actual: ISearchPathsResult, expected: ISea cleanUndefinedQueryValues(actual); assert.deepEqual(actual.includePattern, expected.includePattern, message); - assert.equal(actual.searchPaths && actual.searchPaths.length, expected.searchPaths && expected.searchPaths.length, message); + assert.equal(actual.searchPaths && actual.searchPaths.length, expected.searchPaths && expected.searchPaths.length); actual.searchPaths.forEach((searchPath, i) => { const expectedSearchPath = expected.searchPaths[i]; - assert.equal(searchPath.pattern, expectedSearchPath.pattern, message); - assert.equal(searchPath.searchPath.toString(), expectedSearchPath.searchPath.toString(), message); + assert.equal(searchPath.pattern, expectedSearchPath.pattern); + assert.equal(searchPath.searchPath.toString(), expectedSearchPath.searchPath.toString()); }); } From 3afa94a40449f23f3558a5049f41996403f4d092 Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Thu, 20 Jul 2017 16:04:42 -0700 Subject: [PATCH 026/136] Fix querybuilder tests for win32 --- .../search/test/common/queryBuilder.test.ts | 49 ++++++++++++------- 1 file changed, 30 insertions(+), 19 deletions(-) diff --git a/src/vs/workbench/parts/search/test/common/queryBuilder.test.ts b/src/vs/workbench/parts/search/test/common/queryBuilder.test.ts index 3d860194a25..69f0e389d5e 100644 --- a/src/vs/workbench/parts/search/test/common/queryBuilder.test.ts +++ b/src/vs/workbench/parts/search/test/common/queryBuilder.test.ts @@ -6,6 +6,7 @@ import * as assert from 'assert'; import { IExpression } from 'vs/base/common/glob'; +import * as paths from 'vs/base/common/paths'; import uri from 'vs/base/common/uri'; import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; @@ -16,9 +17,9 @@ import { TestContextService } from 'vs/workbench/test/workbenchTestServices'; import { ISearchQuery, QueryType, IPatternInfo } from 'vs/platform/search/common/search'; -suite('SearchQuery', () => { +suite.only('SearchQuery', () => { const PATTERN_INFO: IPatternInfo = { pattern: 'a' }; - const ROOT_1 = `/foo/root1`; + const ROOT_1 = fixPath('/foo/root1'); const ROOT_1_URI = uri.parse(ROOT_1); let instantiationService: TestInstantiationService; @@ -164,7 +165,7 @@ suite('SearchQuery', () => { './a', { searchPaths: [{ - searchPath: uri.parse(ROOT_1 + '/a') + searchPath: getUri(ROOT_1 + '/a') }] } ], @@ -172,7 +173,7 @@ suite('SearchQuery', () => { './a/*b/c', { searchPaths: [{ - searchPath: uri.parse(ROOT_1 + '/a'), + searchPath: getUri(ROOT_1 + '/a'), pattern: '*b/c' }] } @@ -182,11 +183,11 @@ suite('SearchQuery', () => { { searchPaths: [ { - searchPath: uri.parse(ROOT_1 + '/a'), + searchPath: getUri(ROOT_1 + '/a'), pattern: '*b/c' }, { - searchPath: uri.parse('/project/foo') + searchPath: getUri('/project/foo') }] } ] @@ -195,14 +196,14 @@ suite('SearchQuery', () => { test('relative includes w/two root folders', () => { const ROOT_2 = '/project/root2'; - mockWorkspace.roots = [ROOT_1_URI, uri.parse(ROOT_2)]; + mockWorkspace.roots = [ROOT_1_URI, getUri(ROOT_2)]; [ [ './root1', { searchPaths: [{ - searchPath: uri.parse(ROOT_1) + searchPath: getUri(ROOT_1) }] } ], @@ -210,7 +211,7 @@ suite('SearchQuery', () => { './root2', { searchPaths: [{ - searchPath: uri.parse(ROOT_2), + searchPath: getUri(ROOT_2), }] } ], @@ -219,11 +220,11 @@ suite('SearchQuery', () => { { searchPaths: [ { - searchPath: uri.parse(ROOT_1 + '/a'), + searchPath: getUri(ROOT_1 + '/a'), pattern: '**/b' }, { - searchPath: uri.parse(ROOT_2), + searchPath: getUri(ROOT_2), pattern: '**/*.txt' }] } @@ -234,14 +235,14 @@ suite('SearchQuery', () => { test('relative includes w/multiple ambiguous root folders', () => { const ROOT_2 = '/project/rootB'; const ROOT_3 = '/otherproject/rootB'; - mockWorkspace.roots = [ROOT_1_URI, uri.parse(ROOT_2), uri.parse(ROOT_3)]; + mockWorkspace.roots = [ROOT_1_URI, getUri(ROOT_2), getUri(ROOT_3)]; [ [ './root1', { searchPaths: [{ - searchPath: uri.parse(ROOT_1) + searchPath: getUri(ROOT_1) }] } ], @@ -250,10 +251,10 @@ suite('SearchQuery', () => { { searchPaths: [ { - searchPath: uri.parse(ROOT_2), + searchPath: getUri(ROOT_2), }, { - searchPath: uri.parse(ROOT_3), + searchPath: getUri(ROOT_3), }] } ], @@ -262,19 +263,19 @@ suite('SearchQuery', () => { { searchPaths: [ { - searchPath: uri.parse(ROOT_2 + '/a'), + searchPath: getUri(ROOT_2 + '/a'), pattern: '**/b' }, { - searchPath: uri.parse(ROOT_3 + '/a'), + searchPath: getUri(ROOT_3 + '/a'), pattern: '**/b' }, { - searchPath: uri.parse(ROOT_2 + '/b'), + searchPath: getUri(ROOT_2 + '/b'), pattern: '**/*.txt' }, { - searchPath: uri.parse(ROOT_3 + '/b'), + searchPath: getUri(ROOT_3 + '/b'), pattern: '**/*.txt' }] } @@ -326,3 +327,13 @@ function patternsToIExpression(...patterns: string[]): IExpression { patterns.reduce((glob, cur) => { glob[cur] = true; return glob; }, Object.create(null)) : undefined; } + +function getUri(slashPath: string): uri { + return uri.parse(fixPath(slashPath)); +} + +function fixPath(slashPath: string): string { + return process.platform === 'win32' ? + paths.join('c:', ...slashPath.split('/')) : + slashPath; +} \ No newline at end of file From 75fd5a7f29602537705a221006aeee7fa1445a9d Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Mon, 24 Jul 2017 17:37:00 -0700 Subject: [PATCH 027/136] Fix QueryBuilder issues with trailing / and file:/// --- .../parts/search/common/queryBuilder.ts | 10 ++++- .../search/test/common/queryBuilder.test.ts | 38 +++++++++---------- 2 files changed, 27 insertions(+), 21 deletions(-) diff --git a/src/vs/workbench/parts/search/common/queryBuilder.ts b/src/vs/workbench/parts/search/common/queryBuilder.ts index 4d9052311cd..bc7445ab0b7 100644 --- a/src/vs/workbench/parts/search/common/queryBuilder.ts +++ b/src/vs/workbench/parts/search/common/queryBuilder.ts @@ -141,7 +141,7 @@ export class QueryBuilder { const pathPortions = this.expandAbsoluteSearchPaths(pathPortion); return pathPortions.map(searchPath => { return { - searchPath: uri.parse(searchPath), + searchPath: uri.file(searchPath), pattern: globPortion }; }); @@ -198,8 +198,14 @@ function splitGlobFromPath(searchPath: string): { pathPortion: string, globPorti const globCharIdx = globCharMatch.index; const lastSlashMatch = searchPath.substr(0, globCharIdx).match(/[/|\\][^/\\]*$/); if (lastSlashMatch) { + let pathPortion = searchPath.substr(0, lastSlashMatch.index); + if (!pathPortion.match(/[/\\]/)) { + // If the last slash was the only slash, then we now have '' or 'C:'. Append a slash. + pathPortion += '/'; + } + return { - pathPortion: searchPath.substr(0, lastSlashMatch.index) || '/', + pathPortion, globPortion: searchPath.substr(lastSlashMatch.index + 1) }; } diff --git a/src/vs/workbench/parts/search/test/common/queryBuilder.test.ts b/src/vs/workbench/parts/search/test/common/queryBuilder.test.ts index 69f0e389d5e..2ae2bdf0764 100644 --- a/src/vs/workbench/parts/search/test/common/queryBuilder.test.ts +++ b/src/vs/workbench/parts/search/test/common/queryBuilder.test.ts @@ -17,10 +17,10 @@ import { TestContextService } from 'vs/workbench/test/workbenchTestServices'; import { ISearchQuery, QueryType, IPatternInfo } from 'vs/platform/search/common/search'; -suite.only('SearchQuery', () => { +suite('QueryBuilder', () => { const PATTERN_INFO: IPatternInfo = { pattern: 'a' }; const ROOT_1 = fixPath('/foo/root1'); - const ROOT_1_URI = uri.parse(ROOT_1); + const ROOT_1_URI = getUri(ROOT_1); let instantiationService: TestInstantiationService; let queryBuilder: QueryBuilder; @@ -102,56 +102,56 @@ suite.only('SearchQuery', () => { test('absolute includes', () => { [ [ - '/foo/bar', + fixPath('/foo/bar'), { - searchPaths: [{ searchPath: uri.parse('/foo/bar') }] + searchPaths: [{ searchPath: getUri('/foo/bar') }] } ], [ - '/foo/bar,a', + fixPath('/foo/bar') + ',' + 'a', { - searchPaths: [{ searchPath: uri.parse('/foo/bar') }], + searchPaths: [{ searchPath: getUri('/foo/bar') }], includePattern: patternsToIExpression(globalGlob('a')) } ], [ - '/foo/bar, /1/2', + fixPath('/foo/bar') + ',' + fixPath('/1/2'), { - searchPaths: [{ searchPath: uri.parse('/foo/bar') }, { searchPath: uri.parse('/1/2') }] + searchPaths: [{ searchPath: getUri('/foo/bar') }, { searchPath: getUri('/1/2') }] } ], [ - '/foo/bar/**/*.ts', + fixPath('/foo/bar/**/*.ts'), { searchPaths: [{ - searchPath: uri.parse('/foo/bar'), + searchPath: getUri('/foo/bar'), pattern: '**/*.ts' }] } ], [ - '/foo/bar/*a/b/c', + fixPath('/foo/bar/*a/b/c'), { searchPaths: [{ - searchPath: uri.parse('/foo/bar'), + searchPath: getUri('/foo/bar'), pattern: '*a/b/c' }] } ], [ - '/*a/b/c', + fixPath('/*a/b/c'), { searchPaths: [{ - searchPath: uri.parse('/'), + searchPath: getUri('/'), pattern: '*a/b/c' }] } ], [ - '/foo/{b,c}ar', + fixPath('/foo/{b,c}ar'), { searchPaths: [{ - searchPath: uri.parse('/foo'), + searchPath: getUri('/foo'), pattern: '{b,c}ar' }] } @@ -179,7 +179,7 @@ suite.only('SearchQuery', () => { } ], [ - './a/*b/c, /project/foo', + './a/*b/c, ' + fixPath('/project/foo'), { searchPaths: [ { @@ -329,11 +329,11 @@ function patternsToIExpression(...patterns: string[]): IExpression { } function getUri(slashPath: string): uri { - return uri.parse(fixPath(slashPath)); + return uri.file(fixPath(slashPath)); } function fixPath(slashPath: string): string { return process.platform === 'win32' ? - paths.join('c:', ...slashPath.split('/')) : + (slashPath.match(/^c:/) ? slashPath : paths.join('c:', ...slashPath.split('/'))) : slashPath; } \ No newline at end of file From 6a10043e8dd7ea80f06107d5ad520d74e7cb37ba Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Mon, 24 Jul 2017 15:29:01 -0700 Subject: [PATCH 028/136] Block svg images in markdown preview by default --- extensions/markdown/media/csp.js | 12 +++++++++- extensions/markdown/src/extension.ts | 11 +++++---- extensions/markdown/src/security.ts | 15 ++++++++++++ .../parts/html/browser/html.contribution.ts | 24 +++++++++++++++---- .../parts/html/browser/htmlPreviewPart.ts | 9 ++++++- .../workbench/parts/html/browser/webview.ts | 9 ++++++- .../workbench/parts/html/common/htmlInput.ts | 12 ++++++++++ 7 files changed, 80 insertions(+), 12 deletions(-) diff --git a/extensions/markdown/media/csp.js b/extensions/markdown/media/csp.js index f43b14c8d77..192cb907578 100644 --- a/extensions/markdown/media/csp.js +++ b/extensions/markdown/media/csp.js @@ -11,7 +11,7 @@ let didShow = false; - document.addEventListener('securitypolicyviolation', () => { + const showCspWarning = () => { if (didShow) { return; } @@ -28,5 +28,15 @@ notification.setAttribute('href', `command:markdown.showPreviewSecuritySelector?${encodeURIComponent(JSON.stringify(args))}`); document.body.appendChild(notification); + }; + + document.addEventListener('securitypolicyviolation', () => { + showCspWarning(); + }); + + window.addEventListener('message', (event) => { + if (event && event.data && event.data.name === 'vscode-did-block-svg') { + showCspWarning(); + } }); }()); \ No newline at end of file diff --git a/extensions/markdown/src/extension.ts b/extensions/markdown/src/extension.ts index d4b879e1619..aeaeb51962e 100644 --- a/extensions/markdown/src/extension.ts +++ b/extensions/markdown/src/extension.ts @@ -98,8 +98,8 @@ export function activate(context: vscode.ExtensionContext) { context.subscriptions.push(vscode.languages.registerDocumentLinkProvider('markdown', new DocumentLinkProvider())); - context.subscriptions.push(vscode.commands.registerCommand('markdown.showPreview', showPreview)); - context.subscriptions.push(vscode.commands.registerCommand('markdown.showPreviewToSide', uri => showPreview(uri, true))); + context.subscriptions.push(vscode.commands.registerCommand('markdown.showPreview', (uri) => showPreview(cspArbiter, uri, false))); + context.subscriptions.push(vscode.commands.registerCommand('markdown.showPreviewToSide', uri => showPreview(cspArbiter, uri, true))); context.subscriptions.push(vscode.commands.registerCommand('markdown.showSource', showSource)); context.subscriptions.push(vscode.commands.registerCommand('_markdown.revealLine', (uri, line) => { @@ -206,7 +206,7 @@ export function activate(context: vscode.ExtensionContext) { } -function showPreview(uri?: vscode.Uri, sideBySide: boolean = false) { +function showPreview(cspArbiter: ExtensionContentSecurityPolicyArbiter, uri?: vscode.Uri, sideBySide: boolean = false) { let resource = uri; if (!(resource instanceof vscode.Uri)) { if (vscode.window.activeTextEditor) { @@ -228,7 +228,10 @@ function showPreview(uri?: vscode.Uri, sideBySide: boolean = false) { getMarkdownUri(resource), getViewColumn(sideBySide), `Preview '${path.basename(resource.fsPath)}'`, - { allowScripts: true, allowSvgs: true }); + { + allowScripts: true, + allowSvgs: cspArbiter.shouldAllowSvgsForResource(resource) + }); if (telemetryReporter) { telemetryReporter.sendTelemetryEvent('openPreview', { diff --git a/extensions/markdown/src/security.ts b/extensions/markdown/src/security.ts index 5011d5feb47..b3776e4087b 100644 --- a/extensions/markdown/src/security.ts +++ b/extensions/markdown/src/security.ts @@ -22,6 +22,8 @@ export interface ContentSecurityPolicyArbiter { getSecurityLevelForResource(resource: vscode.Uri): MarkdownPreviewSecurityLevel; setSecurityLevelForResource(resource: vscode.Uri, level: MarkdownPreviewSecurityLevel): Thenable; + + shouldAllowSvgsForResource(resource: vscode.Uri): void; } export class ExtensionContentSecurityPolicyArbiter implements ContentSecurityPolicyArbiter { @@ -50,6 +52,11 @@ export class ExtensionContentSecurityPolicyArbiter implements ContentSecurityPol return this.globalState.update(this.security_level_key + this.getRoot(resource), level); } + public shouldAllowSvgsForResource(resource: vscode.Uri) { + const securityLevel = this.getSecurityLevelForResource(resource); + return securityLevel === MarkdownPreviewSecurityLevel.AllowInsecureContent || securityLevel === MarkdownPreviewSecurityLevel.AllowScriptsAndAllContent; + } + private getRoot(resource: vscode.Uri): vscode.Uri { if (vscode.workspace.workspaceFolders) { const folderForResource = vscode.workspace.getWorkspaceFolder(resource); @@ -123,6 +130,14 @@ export class PreviewSecuritySelector { await this.cspArbiter.setSecurityLevelForResource(resource, selection.level); const sourceUri = getMarkdownUri(resource); + + await vscode.commands.executeCommand('_workbench.htmlPreview.updateOptions', + sourceUri, + { + allowScripts: true, + allowSvgs: this.cspArbiter.shouldAllowSvgsForResource(resource) + }); + this.contentProvider.update(sourceUri); } } diff --git a/src/vs/workbench/parts/html/browser/html.contribution.ts b/src/vs/workbench/parts/html/browser/html.contribution.ts index ddb472e6a37..40b498fa282 100644 --- a/src/vs/workbench/parts/html/browser/html.contribution.ts +++ b/src/vs/workbench/parts/html/browser/html.contribution.ts @@ -21,6 +21,14 @@ import { IEditorGroupService } from 'vs/workbench/services/group/common/groupSer import { MenuRegistry } from "vs/platform/actions/common/actions"; import { WebviewElement } from "vs/workbench/parts/html/browser/webview"; +function getActivePreviewsForResource(accessor: ServicesAccessor, resource: URI | string) { + const uri = resource instanceof URI ? resource : URI.parse(resource); + return accessor.get(IWorkbenchEditorService).getVisibleEditors() + .filter(c => c instanceof HtmlPreviewPart && c.model) + .map(e => e as HtmlPreviewPart) + .filter(e => e.model.uri.scheme === uri.scheme && e.model.uri.fsPath === uri.fsPath); +} + // --- Register Editor (Registry.as(EditorExtensions.Editors)).registerEditor(new EditorDescriptor(HtmlPreviewPart.ID, localize('html.editor.label', "Html Preview"), @@ -70,17 +78,23 @@ CommandsRegistry.registerCommand('_workbench.previewHtml', function ( }); CommandsRegistry.registerCommand('_workbench.htmlPreview.postMessage', (accessor: ServicesAccessor, resource: URI | string, message: any) => { - const uri = resource instanceof URI ? resource : URI.parse(resource); - const activePreviews = accessor.get(IWorkbenchEditorService).getVisibleEditors() - .filter(c => c instanceof HtmlPreviewPart && c.model) - .map(e => e as HtmlPreviewPart) - .filter(e => e.model.uri.scheme === uri.scheme && e.model.uri.fsPath === uri.fsPath); + const activePreviews = getActivePreviewsForResource(accessor, resource); for (const preview of activePreviews) { preview.sendMessage(message); } return activePreviews.length > 0; }); +CommandsRegistry.registerCommand('_workbench.htmlPreview.updateOptions', (accessor: ServicesAccessor, resource: URI | string, options: HtmlInputOptions) => { + const uri = resource instanceof URI ? resource : URI.parse(resource); + const activePreviews = getActivePreviewsForResource(accessor, resource); + for (const preview of activePreviews) { + if (preview.input && preview.input instanceof HtmlInput) { + const input = accessor.get(IInstantiationService).createInstance(HtmlInput, preview.input.getName(), '', uri, options); + preview.setInput(input); + } + } +}); CommandsRegistry.registerCommand('_webview.openDevTools', function () { const elements = document.querySelectorAll('webview.ready'); diff --git a/src/vs/workbench/parts/html/browser/htmlPreviewPart.ts b/src/vs/workbench/parts/html/browser/htmlPreviewPart.ts index 3d956d9044f..e982a70facd 100644 --- a/src/vs/workbench/parts/html/browser/htmlPreviewPart.ts +++ b/src/vs/workbench/parts/html/browser/htmlPreviewPart.ts @@ -14,7 +14,7 @@ import { EditorOptions, EditorInput } from 'vs/workbench/common/editor'; import { Position } from 'vs/platform/editor/common/editor'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { BaseTextEditorModel } from 'vs/workbench/common/editor/textEditorModel'; -import { HtmlInput } from 'vs/workbench/parts/html/common/htmlInput'; +import { HtmlInput, HtmlInputOptions, areHtmlInputOptionsEqual } from 'vs/workbench/parts/html/common/htmlInput'; import { IThemeService } from 'vs/platform/theme/common/themeService'; import { IOpenerService } from 'vs/platform/opener/common/opener'; import { ITextModelService, ITextEditorModel } from 'vs/editor/common/services/resolverService'; @@ -182,7 +182,10 @@ export class HtmlPreviewPart extends WebviewEditor { return TPromise.as(undefined); } + let oldOptions: HtmlInputOptions | undefined = undefined; + if (this.input instanceof HtmlInput) { + oldOptions = this.input.options; this.saveViewState(this.input.getResource(), { scrollYPercentage: this.scrollYPercentage }); @@ -210,6 +213,10 @@ export class HtmlPreviewPart extends WebviewEditor { return TPromise.wrapError(new Error(localize('html.voidInput', "Invalid editor input."))); } + if (oldOptions && !areHtmlInputOptionsEqual(oldOptions, input.options)) { + this._doSetVisible(false); + } + this._modelChangeSubscription = this.model.onDidChangeContent(() => { if (this.model) { this.scrollYPercentage = 0; diff --git a/src/vs/workbench/parts/html/browser/webview.ts b/src/vs/workbench/parts/html/browser/webview.ts index 59d06e85c47..44cb9a0bdb6 100644 --- a/src/vs/workbench/parts/html/browser/webview.ts +++ b/src/vs/workbench/parts/html/browser/webview.ts @@ -122,6 +122,7 @@ export default class Webview { if (details.url.indexOf('.svg') > 0) { const uri = URI.parse(details.url); if (uri && !uri.scheme.match(/file/i) && (uri.path as any).endsWith('.svg') && !this.isAllowedSvg(uri)) { + this.onDidBlockSvg(); return callback({ cancel: true }); } } @@ -133,6 +134,7 @@ export default class Webview { if (contentType && Array.isArray(contentType) && contentType.some(x => x.toLowerCase().indexOf('image/svg') >= 0)) { const uri = URI.parse(details.url); if (uri && !this.isAllowedSvg(uri)) { + this.onDidBlockSvg(); return callback({ cancel: true }); } } @@ -195,7 +197,6 @@ export default class Webview { parent.appendChild(this._webviewFindWidget.getDomNode()); parent.appendChild(this._webview); } - } public notifyFindWidgetFocusChanged(isFocused: boolean) { @@ -259,6 +260,12 @@ export default class Webview { this._send('message', data); } + private onDidBlockSvg() { + this.sendMessage({ + name: 'vscode-did-block-svg' + }); + } + style(theme: ITheme): void { const { fontFamily, fontWeight, fontSize } = window.getComputedStyle(this._styleElement); // TODO@theme avoid styleElement diff --git a/src/vs/workbench/parts/html/common/htmlInput.ts b/src/vs/workbench/parts/html/common/htmlInput.ts index a8d21c8fbee..34b00ed9152 100644 --- a/src/vs/workbench/parts/html/common/htmlInput.ts +++ b/src/vs/workbench/parts/html/common/htmlInput.ts @@ -14,6 +14,10 @@ export interface HtmlInputOptions { allowSvgs?: boolean; } +export function areHtmlInputOptionsEqual(left: HtmlInputOptions, right: HtmlInputOptions) { + return left.allowScripts === right.allowScripts && left.allowSvgs === right.allowSvgs; +} + export class HtmlInput extends ResourceEditorInput { constructor( name: string, @@ -24,4 +28,12 @@ export class HtmlInput extends ResourceEditorInput { ) { super(name, description, resource, textModelResolverService); } + + public matches(otherInput: any): boolean { + if (!super.matches(otherInput)) { + return false; + } + + return otherInput instanceof HtmlInput && areHtmlInputOptionsEqual(this.options, otherInput.options); + } } From bb2e86948956562bc98d94f65c1d03a718461ee1 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Mon, 24 Jul 2017 18:01:11 -0700 Subject: [PATCH 029/136] Show multiple node_modules versions in TSVersion picker --- .../typescript/src/utils/versionPicker.ts | 87 +++++++++---------- .../typescript/src/utils/versionProvider.ts | 40 +++++++-- 2 files changed, 73 insertions(+), 54 deletions(-) diff --git a/extensions/typescript/src/utils/versionPicker.ts b/extensions/typescript/src/utils/versionPicker.ts index 22d4e0a1700..b978b2ef12b 100644 --- a/extensions/typescript/src/utils/versionPicker.ts +++ b/extensions/typescript/src/utils/versionPicker.ts @@ -5,7 +5,7 @@ import * as nls from 'vscode-nls'; import { TypeScriptVersionProvider, TypeScriptVersion } from "./versionProvider"; -import { Memento, commands, Uri, window, QuickPickItem } from "vscode"; +import { Memento, commands, Uri, window, QuickPickItem, workspace } from "vscode"; const localize = nls.loadMessageBundle(); @@ -13,6 +13,7 @@ const useWorkspaceTsdkStorageKey = 'typescript.useWorkspaceTsdk'; interface MyQuickPickItem extends QuickPickItem { id: MessageAction; + version?: TypeScriptVersion; } enum MessageAction { @@ -31,7 +32,7 @@ export class TypeScriptVersionPicker { this._currentVersion = this.versionProvider.defaultVersion; if (workspaceState.get(useWorkspaceTsdkStorageKey, false)) { - const localVersion = this.versionProvider.localVersion; + const localVersion = this.versionProvider.localTsdkVersion; if (localVersion) { this._currentVersion = localVersion; } @@ -46,15 +47,12 @@ export class TypeScriptVersionPicker { this._currentVersion = this.versionProvider.bundledVersion; } - public show(firstRun?: boolean): Thenable<{ oldVersion?: TypeScriptVersion, newVersion?: TypeScriptVersion }> { - const useWorkspaceVersionSetting = this.workspaceState.get(useWorkspaceTsdkStorageKey, false); - const shippedVersion = this.versionProvider.defaultVersion; - const localVersion = this.versionProvider.localVersion; - + public async show(firstRun?: boolean): Promise<{ oldVersion?: TypeScriptVersion, newVersion?: TypeScriptVersion }> { const pickOptions: MyQuickPickItem[] = []; + const shippedVersion = this.versionProvider.defaultVersion; pickOptions.push({ - label: (this.currentVersion.path === shippedVersion.path && (this.currentVersion.path !== (localVersion && localVersion.path) || !useWorkspaceVersionSetting) + label: (this.currentVersion.path === shippedVersion.path ? '• ' : '') + localize('useVSCodeVersionOption', 'Use VSCode\'s Version'), description: shippedVersion.version.versionString, @@ -62,14 +60,15 @@ export class TypeScriptVersionPicker { id: MessageAction.useBundled }); - if (localVersion) { + for (const version of this.versionProvider.localVersions) { pickOptions.push({ - label: ((this.currentVersion.path === localVersion.path && (this.currentVersion.path !== shippedVersion.path || useWorkspaceVersionSetting) + label: (this.currentVersion.path === version.path ? '• ' - : '')) + localize('useWorkspaceVersionOption', 'Use Workspace Version'), - description: localVersion.version.versionString, - detail: localVersion.label, - id: MessageAction.useLocal + : '') + localize('useWorkspaceVersionOption', 'Use Workspace Version'), + description: version.version.versionString, + detail: version.label, + id: MessageAction.useLocal, + version: version }); } @@ -79,45 +78,43 @@ export class TypeScriptVersionPicker { id: MessageAction.learnMore }); - return window.showQuickPick(pickOptions, { + const selected = await window.showQuickPick(pickOptions, { placeHolder: localize( 'selectTsVersion', 'Select the TypeScript version used for JavaScript and TypeScript language features'), ignoreFocusOut: firstRun - }) - .then(selected => { - if (!selected) { - return { oldVersion: this.currentVersion }; + }); + + if (!selected) { + return { oldVersion: this.currentVersion }; + } + + switch (selected.id) { + case MessageAction.useLocal: + await this.workspaceState.update(useWorkspaceTsdkStorageKey, true); + if (selected.version) { + const tsConfig = workspace.getConfiguration('typescript'); + await tsConfig.update('tsdk', selected.version.label, false); + + const previousVersion = this.currentVersion; + this._currentVersion = selected.version; + return { oldVersion: previousVersion, newVersion: selected.version }; } - switch (selected.id) { - case MessageAction.useLocal: - return this.workspaceState.update(useWorkspaceTsdkStorageKey, true) - .then(_ => { - if (localVersion) { - const previousVersion = this.currentVersion; + return { oldVersion: this.currentVersion }; - this._currentVersion = localVersion; - return { oldVersion: previousVersion, newVersion: localVersion }; - } - return { oldVersion: this.currentVersion }; - }); + case MessageAction.useBundled: + await this.workspaceState.update(useWorkspaceTsdkStorageKey, false); + const previousVersion = this.currentVersion; + this._currentVersion = shippedVersion; + return { oldVersion: previousVersion, newVersion: shippedVersion }; - case MessageAction.useBundled: - return this.workspaceState.update(useWorkspaceTsdkStorageKey, false) - .then(_ => { - const previousVersion = this.currentVersion; - this._currentVersion = shippedVersion; - return { oldVersion: previousVersion, newVersion: shippedVersion }; - }); - case MessageAction.learnMore: - commands.executeCommand('vscode.open', Uri.parse('https://go.microsoft.com/fwlink/?linkid=839919')); - return { oldVersion: this.currentVersion }; - - default: - return { oldVersion: this.currentVersion }; - } - }); + case MessageAction.learnMore: + commands.executeCommand('vscode.open', Uri.parse('https://go.microsoft.com/fwlink/?linkid=839919')); + return { oldVersion: this.currentVersion }; + default: + return { oldVersion: this.currentVersion }; + } } } \ No newline at end of file diff --git a/extensions/typescript/src/utils/versionProvider.ts b/extensions/typescript/src/utils/versionProvider.ts index 02698ded04c..3e5e577e90c 100644 --- a/extensions/typescript/src/utils/versionProvider.ts +++ b/extensions/typescript/src/utils/versionProvider.ts @@ -44,19 +44,36 @@ export class TypeScriptVersionProvider { return undefined; } - public get localVersion(): TypeScriptVersion | undefined { + public get localTsdkVersion(): TypeScriptVersion | undefined { const tsdkVersions = this.localTsdkVersions; if (tsdkVersions && tsdkVersions.length) { return tsdkVersions[0]; } const nodeVersions = this.localNodeModulesVersions; - if (nodeVersions && nodeVersions.length) { + if (nodeVersions && nodeVersions.length === 1) { return nodeVersions[0]; } return undefined; } + public get localVersions(): TypeScriptVersion[] { + const versions: TypeScriptVersion[] = []; + const tsdkVersions = this.localTsdkVersions; + if (tsdkVersions && tsdkVersions.length) { + versions.push(tsdkVersions[0]); + } + + const paths = new Set(); + return versions.concat(this.localNodeModulesVersions).filter(x => { + if (paths.has(x.path)) { + return false; + } + paths.add(x.path); + return true; + }); + } + public get bundledVersion(): TypeScriptVersion { try { const bundledVersion = this.loadFromPath(require.resolve('typescript/lib/tsserver.js')); @@ -83,12 +100,17 @@ export class TypeScriptVersionProvider { } for (const root of workspace.workspaceFolders || []) { - const rootPrefix = `./${root.name}/`; - const winRootPrefix = `.\\${root.name}\\`; - - if (tsdkPathSetting.startsWith(rootPrefix) || tsdkPathSetting.startsWith(winRootPrefix)) { - const workspacePath = path.join(root.uri.fsPath, tsdkPathSetting.replace(rootPrefix, '')); - return this.getTypeScriptsFromPaths(workspacePath); + const rootPrefixes = [`./${root.name}/`, `${root.name}/`, `.\\${root.name}\\`, `${root.name}\\`]; + for (const rootPrefix of rootPrefixes) { + if (tsdkPathSetting.startsWith(rootPrefix)) { + const workspacePath = path.join(root.uri.fsPath, tsdkPathSetting.replace(rootPrefix, '')); + const version = this.loadFromPath(path.join(workspacePath, 'tsserver.js')); + if (version) { + version.label = tsdkPathSetting; + return [version]; + } + return []; + } } } @@ -110,7 +132,7 @@ export class TypeScriptVersionProvider { } const versions: TypeScriptVersion[] = []; - for (const root of [workspace.workspaceFolders[0]]) { + for (const root of workspace.workspaceFolders) { const p = path.join(root.uri.fsPath, typeScriptPath, 'tsserver.js'); let label: string | undefined = undefined; From 686d6959ea28a328fe5ee3ea9aa30150aa7c8c15 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Mon, 24 Jul 2017 18:25:10 -0700 Subject: [PATCH 030/136] Kill process tree when killing terminal Fixes #26807 --- .../terminal/electron-browser/terminalInstance.ts | 2 +- .../workbench/parts/terminal/node/terminalProcess.ts | 12 ++++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/parts/terminal/electron-browser/terminalInstance.ts b/src/vs/workbench/parts/terminal/electron-browser/terminalInstance.ts index 0398558d40b..94580d950da 100644 --- a/src/vs/workbench/parts/terminal/electron-browser/terminalInstance.ts +++ b/src/vs/workbench/parts/terminal/electron-browser/terminalInstance.ts @@ -414,7 +414,7 @@ export class TerminalInstance implements ITerminalInstance { } if (this._process) { if (this._process.connected) { - this._process.kill(); + this._process.send({ event: 'shutdown' }); } this._process = null; } diff --git a/src/vs/workbench/parts/terminal/node/terminalProcess.ts b/src/vs/workbench/parts/terminal/node/terminalProcess.ts index ab1589b4dcf..aa4f085c7bb 100644 --- a/src/vs/workbench/parts/terminal/node/terminalProcess.ts +++ b/src/vs/workbench/parts/terminal/node/terminalProcess.ts @@ -3,6 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import * as cp from 'child_process'; import * as os from 'os'; import * as path from 'path'; import * as pty from 'node-pty'; @@ -51,7 +52,16 @@ var exitCode; // Allow any trailing data events to be sent before the exit event is sent. // See https://github.com/Tyriar/node-pty/issues/72 function queueProcessExit() { + if (closeTimeout) { + clearTimeout(closeTimeout); + } closeTimeout = setTimeout(function () { + if (process.platform === 'win32') { + // Forcefully kill the entire process tree under the shell process + // on Windows as ptyProcess.kill can leave some lingering processes. + // See https://github.com/Microsoft/vscode/issues/26807 + cp.execFileSync('taskkill.exe', ['/T', '/F', '/PID', ptyProcess.pid.toString()]); + } ptyProcess.kill(); process.exit(exitCode); }, 250); @@ -78,6 +88,8 @@ process.on('message', function (message) { ptyProcess.write(message.data); } else if (message.event === 'resize') { ptyProcess.resize(message.cols, message.rows); + } else if (message.event === 'shutdown') { + queueProcessExit(); } }); From 764dd415ad591f4eb4c78ee8189552b48a502c08 Mon Sep 17 00:00:00 2001 From: Ramya Achutha Rao Date: Mon, 24 Jul 2017 18:34:55 -0700 Subject: [PATCH 031/136] Support multi cursor in Emmet Update Image Size --- .../emmet/src/test/updateImageSize.test.ts | 150 ++++++++++++++++++ extensions/emmet/src/updateImageSize.ts | 92 ++++++----- 2 files changed, 198 insertions(+), 44 deletions(-) create mode 100644 extensions/emmet/src/test/updateImageSize.test.ts diff --git a/extensions/emmet/src/test/updateImageSize.test.ts b/extensions/emmet/src/test/updateImageSize.test.ts new file mode 100644 index 00000000000..2a93c777bf9 --- /dev/null +++ b/extensions/emmet/src/test/updateImageSize.test.ts @@ -0,0 +1,150 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as assert from 'assert'; +import { Selection, commands } from 'vscode'; +import { withRandomFileEditor, closeAllEditors } from './testUtils'; +import * as path from 'path'; + +suite('Tests for Emmet actions on html tags', () => { + teardown(closeAllEditors); + const filePath = path.join(__dirname, '../../../../resources/linux/code.png'); + + test('update image css with multiple cursors in css file', () => { + const cssContents = ` + .one { + margin: 10px; + padding: 10px; + background-image: url(https://github.com/Microsoft/vscode/blob/master/resources/linux/code.png); + } + .two { + background-image: url(https://github.com/Microsoft/vscode/blob/master/resources/linux/code.png); + height: 42px; + } + .three { + background-image: url(${filePath}); + width: 42px; + } + `; + const expectedContents = ` + .one { + margin: 10px; + padding: 10px; + background-image: url(https://github.com/Microsoft/vscode/blob/master/resources/linux/code.png); + width: 32px; + height: 32px; + } + .two { + background-image: url(https://github.com/Microsoft/vscode/blob/master/resources/linux/code.png); + width: 32px; + height: 32px; + } + .three { + background-image: url(${filePath}); + height: 1024px; + width: 1024px; + } + `; + return withRandomFileEditor(cssContents, 'css', (editor, doc) => { + editor.selections = [ + new Selection(4, 50, 4, 50), + new Selection(7, 50, 7, 50), + new Selection(11, 50, 11, 50) + ]; + + return commands.executeCommand('emmet.updateImageSize').then(() => { + assert.equal(doc.getText(), expectedContents); + return Promise.resolve(); + }); + }); + }); + + test('update image size in css in html file with multiple cursors', () => { + const htmlWithCssContents = ` + + + + `; + const expectedContents = ` + + + + `; + return withRandomFileEditor(htmlWithCssContents, 'html', (editor, doc) => { + editor.selections = [ + new Selection(6, 50, 6, 50), + new Selection(9, 50, 9, 50), + new Selection(13, 50, 13, 50) + ]; + + return commands.executeCommand('emmet.updateImageSize').then(() => { + assert.equal(doc.getText(), expectedContents); + return Promise.resolve(); + }); + }); + }); + + test('update image size in img tag in html file with multiple cursors', () => { + const htmlwithimgtag = ` + + + + + + `; + const expectedContents = ` + + + + + + `; + return withRandomFileEditor(htmlwithimgtag, 'html', (editor, doc) => { + editor.selections = [ + new Selection(2, 50, 2, 50), + new Selection(3, 50, 3, 50), + new Selection(4, 50, 4, 50) + ]; + + return commands.executeCommand('emmet.updateImageSize').then(() => { + assert.equal(doc.getText(), expectedContents); + return Promise.resolve(); + }); + }); + }); + +}); diff --git a/extensions/emmet/src/updateImageSize.ts b/extensions/emmet/src/updateImageSize.ts index 46edeee044d..11471c888e9 100644 --- a/extensions/emmet/src/updateImageSize.ts +++ b/extensions/emmet/src/updateImageSize.ts @@ -27,79 +27,92 @@ export function updateImageSize() { return; } - if (!isStyleSheet(editor.document.languageId)) { - return updateImageSizeHTML(editor); - } else { - return updateImageSizeCSSFile(editor); - } + let allUpdatesPromise = editor.selections.reverse().map(selection => { + let position = selection.isReversed ? selection.active : selection.anchor; + if (!isStyleSheet(editor.document.languageId)) { + return updateImageSizeHTML(editor, position); + } else { + return updateImageSizeCSSFile(editor, position); + } + }); + + return Promise.all(allUpdatesPromise).then((updates) => { + return editor.edit(builder => { + updates.forEach(update => { + update.forEach(([rangeToReplace, textToReplace]) => { + builder.replace(rangeToReplace, textToReplace); + }); + }); + }); + }); } /** * Updates image size of context tag of HTML model */ -function updateImageSizeHTML(editor: TextEditor) { - const src = getImageSrcHTML(getImageHTMLNode(editor)); +function updateImageSizeHTML(editor: TextEditor, position: Position): Promise<[Range, string][]> { + const src = getImageSrcHTML(getImageHTMLNode(editor, position)); if (!src) { - return updateImageSizeStyleTag(editor); + return updateImageSizeStyleTag(editor, position); } - locateFile(path.dirname(editor.document.fileName), src) + return locateFile(path.dirname(editor.document.fileName), src) .then(getImageSize) .then((size: any) => { // since this action is asynchronous, we have to ensure that editor wasn’t // changed and user didn’t moved caret outside node - const img = getImageHTMLNode(editor); + const img = getImageHTMLNode(editor, position); if (getImageSrcHTML(img) === src) { - updateHTMLTag(editor, img, size.width, size.height); + return updateHTMLTag(editor, img, size.width, size.height); } }) - .catch(err => console.warn('Error while updating image size:', err)); + .catch(err => { console.warn('Error while updating image size:', err); return []; }); } -function updateImageSizeStyleTag(editor: TextEditor) { +function updateImageSizeStyleTag(editor: TextEditor, position: Position): Promise<[Range, string][]> { let getPropertyInsiderStyleTag = (editor) => { const rootNode = parseDocument(editor.document); - const currentNode = getNode(rootNode, editor.selection.active); + const currentNode = getNode(rootNode, position); if (currentNode && currentNode.name === 'style' - && currentNode.open.end.isBefore(editor.selection.active) - && currentNode.close.start.isAfter(editor.selection.active)) { + && currentNode.open.end.isBefore(position) + && currentNode.close.start.isAfter(position)) { let buffer = new DocumentStreamReader(editor.document, currentNode.start, new Range(currentNode.start, currentNode.end)); let rootNode = parseStylesheet(buffer); - const node = getNode(rootNode, editor.selection.active); + const node = getNode(rootNode, position); return (node && node.type === 'property') ? node : null; } }; - return updateImageSizeCSS(editor, getPropertyInsiderStyleTag); + return updateImageSizeCSS(editor, position, getPropertyInsiderStyleTag); } -function updateImageSizeCSSFile(editor: TextEditor) { - return updateImageSizeCSS(editor, getImageCSSNode); +function updateImageSizeCSSFile(editor: TextEditor, position: Position): Promise<[Range, string][]> { + return updateImageSizeCSS(editor, position, getImageCSSNode); } /** * Updates image size of context rule of stylesheet model */ -function updateImageSizeCSS(editor: TextEditor, fetchNode: (editor) => Property) { +function updateImageSizeCSS(editor: TextEditor, position: Position, fetchNode: (editor, position) => Property): Promise<[Range, string][]> { - const src = getImageSrcCSS(fetchNode(editor), editor.selection.active); + const src = getImageSrcCSS(fetchNode(editor, position), position); if (!src) { return Promise.reject(new Error('No valid image source')); } - locateFile(path.dirname(editor.document.fileName), src) + return locateFile(path.dirname(editor.document.fileName), src) .then(getImageSize) .then((size: any) => { // since this action is asynchronous, we have to ensure that editor wasn’t // changed and user didn’t moved caret outside node - const prop = fetchNode(editor); - if (getImageSrcCSS(prop, editor.selection.active) === src) { - updateCSSNode(editor, prop, size.width, size.height); + const prop = fetchNode(editor, position); + if (getImageSrcCSS(prop, position) === src) { + return updateCSSNode(editor, prop, size.width, size.height); } }) - .catch(err => console.warn('Error while updating image size:', err)); + .catch(err => { console.warn('Error while updating image size:', err); return []; }); } /** @@ -108,9 +121,9 @@ function updateImageSizeCSS(editor: TextEditor, fetchNode: (editor) => Property) * @param {TextEditor} editor * @return {HtmlNode} */ -function getImageHTMLNode(editor: TextEditor): HtmlNode { +function getImageHTMLNode(editor: TextEditor, position: Position): HtmlNode { const rootNode = parseDocument(editor.document); - const node = getNode(rootNode, editor.selection.active, true); + const node = getNode(rootNode, position, true); return node && node.name.toLowerCase() === 'img' ? node : null; } @@ -121,9 +134,9 @@ function getImageHTMLNode(editor: TextEditor): HtmlNode { * @param {TextEditor} editor * @return {Property} */ -function getImageCSSNode(editor: TextEditor): Property { +function getImageCSSNode(editor: TextEditor, position: Position): Property { const rootNode = parseDocument(editor.document); - const node = getNode(rootNode, editor.selection.active, true); + const node = getNode(rootNode, position, true); return node && node.type === 'property' ? node : null; } @@ -135,7 +148,6 @@ function getImageCSSNode(editor: TextEditor): Property { function getImageSrcHTML(node: HtmlNode): string { const srcAttr = getAttribute(node, 'src'); if (!srcAttr) { - console.warn('No "src" attribute in', node && node.open); return; } @@ -173,7 +185,7 @@ function getImageSrcCSS(node: Property, position: Position): string { * @param {number} width * @param {number} height */ -function updateHTMLTag(editor: TextEditor, node: HtmlNode, width: number, height: number) { +function updateHTMLTag(editor: TextEditor, node: HtmlNode, width: number, height: number): [Range, string][] { const srcAttr = getAttribute(node, 'src'); const widthAttr = getAttribute(node, 'width'); const heightAttr = getAttribute(node, 'height'); @@ -197,11 +209,7 @@ function updateHTMLTag(editor: TextEditor, node: HtmlNode, width: number, height edits.push([new Range(endOfAttributes, endOfAttributes), textToAdd]); } - return editor.edit(builder => { - edits.forEach(([rangeToReplace, textToReplace]) => { - builder.replace(rangeToReplace, textToReplace); - }); - }); + return edits; } /** @@ -211,7 +219,7 @@ function updateHTMLTag(editor: TextEditor, node: HtmlNode, width: number, height * @param {number} width * @param {number} height */ -function updateCSSNode(editor: TextEditor, srcProp: Property, width: number, height: number) { +function updateCSSNode(editor: TextEditor, srcProp: Property, width: number, height: number): [Range, string][] { const rule = srcProp.parent; const widthProp = getCssPropertyFromRule(rule, 'width'); const heightProp = getCssPropertyFromRule(rule, 'height'); @@ -240,11 +248,7 @@ function updateCSSNode(editor: TextEditor, srcProp: Property, width: number, hei edits.push([new Range(srcProp.end, srcProp.end), textToAdd]); } - return editor.edit(builder => { - edits.forEach(([rangeToReplace, textToReplace]) => { - builder.replace(rangeToReplace, textToReplace); - }); - }); + return edits; } /** From bbfdd06393ebfcf3d6018083440855ca9cdff1e7 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Mon, 24 Jul 2017 18:40:15 -0700 Subject: [PATCH 032/136] Uplevel xterm.js Bring in lineFeed event for #30152, #31155 --- npm-shrinkwrap.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/npm-shrinkwrap.json b/npm-shrinkwrap.json index ab8c41c10cf..e35567fe089 100644 --- a/npm-shrinkwrap.json +++ b/npm-shrinkwrap.json @@ -454,7 +454,7 @@ "xterm": { "version": "2.8.0", "from": "Tyriar/xterm.js#vscode-release/1.15", - "resolved": "git+https://github.com/Tyriar/xterm.js.git#9d95fc1eef596c3d7e8d873a7782e0bcdbfa5503" + "resolved": "git+https://github.com/Tyriar/xterm.js.git#b546de750e22addd8e226b01bd414f5b20ec4018" }, "yauzl": { "version": "2.3.1", From 24ca702d2358970585c885fad9dd0f6784f29a9d Mon Sep 17 00:00:00 2001 From: Ramya Achutha Rao Date: Mon, 24 Jul 2017 19:01:56 -0700 Subject: [PATCH 033/136] Escape $ so that it doesnt get treated as variable Fixes #31032 --- extensions/emmet/src/abbreviationActions.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/extensions/emmet/src/abbreviationActions.ts b/extensions/emmet/src/abbreviationActions.ts index 43b0a46f16a..74d87104cc3 100644 --- a/extensions/emmet/src/abbreviationActions.ts +++ b/extensions/emmet/src/abbreviationActions.ts @@ -17,6 +17,8 @@ interface ExpandAbbreviationInput { preceedingWhiteSpace?: string; } +const selectedTextToWrap = '\n\$TM_SELECTED_TEXT\n'; + export function wrapWithAbbreviation(args) { const syntax = getSyntaxFromArgs(args); if (!syntax || !validate()) { @@ -72,7 +74,7 @@ export function wrapWithAbbreviation(args) { if (!allTextToReplaceSame) { expandAbbrList.forEach(input => { - input.textToWrap = '\n\$TM_SELECTED_TEXT\n'; + input.textToWrap = selectedTextToWrap; }); } @@ -230,6 +232,9 @@ function expandAbbr(input: ExpandAbbreviationInput, newLine: string): string { let expandedText; try { expandedText = expand(input.abbreviation, expandOptions); + if (input.textToWrap !== selectedTextToWrap) { + expandedText = expandedText.replace(/\$/g, '\\$'); + } } catch (e) { vscode.window.showErrorMessage('Failed to expand abbreviation'); } From 5159c7d05d9c5bdc4e323d994d1f7b3da781cb0d Mon Sep 17 00:00:00 2001 From: Ramya Achutha Rao Date: Mon, 24 Jul 2017 19:31:42 -0700 Subject: [PATCH 034/136] Escape $ only when wrapping with abbreviation --- extensions/emmet/src/abbreviationActions.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions/emmet/src/abbreviationActions.ts b/extensions/emmet/src/abbreviationActions.ts index 74d87104cc3..36639adb39b 100644 --- a/extensions/emmet/src/abbreviationActions.ts +++ b/extensions/emmet/src/abbreviationActions.ts @@ -232,7 +232,7 @@ function expandAbbr(input: ExpandAbbreviationInput, newLine: string): string { let expandedText; try { expandedText = expand(input.abbreviation, expandOptions); - if (input.textToWrap !== selectedTextToWrap) { + if (input.textToWrap && input.textToWrap !== selectedTextToWrap) { expandedText = expandedText.replace(/\$/g, '\\$'); } } catch (e) { From 9bbd4493b1b76b8f74c032edf1c3b03fd3aef4ac Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Mon, 24 Jul 2017 20:26:43 -0700 Subject: [PATCH 035/136] Implement search include/exclude precedence per #27226 --- .../services/search/node/ripgrepTextSearch.ts | 56 +++++++++++-------- 1 file changed, 32 insertions(+), 24 deletions(-) diff --git a/src/vs/workbench/services/search/node/ripgrepTextSearch.ts b/src/vs/workbench/services/search/node/ripgrepTextSearch.ts index 6c8ea877d77..437c1ab2f00 100644 --- a/src/vs/workbench/services/search/node/ripgrepTextSearch.ts +++ b/src/vs/workbench/services/search/node/ripgrepTextSearch.ts @@ -11,6 +11,7 @@ import { StringDecoder, NodeStringDecoder } from 'string_decoder'; import * as cp from 'child_process'; import { rgPath } from 'vscode-ripgrep'; +import arrays = require('vs/base/common/arrays'); import objects = require('vs/base/common/objects'); import platform = require('vs/base/common/platform'); import * as strings from 'vs/base/common/strings'; @@ -382,15 +383,27 @@ export class LineMatch implements ILineMatch { interface IRgGlobResult { globArgs: string[]; - siblingClauses: glob.IExpression; + siblingClauses?: glob.IExpression; } -function foldersToRgExcludeGlobs(folderQueries: IFolderSearch[], globalExclude: glob.IExpression): IRgGlobResult { +function foldersToRgExcludeGlobs(folderQueries: IFolderSearch[]): IRgGlobResult { + return foldersToRgGlobs(folderQueries, fq => fq.excludePattern); +} + +function foldersToRgIncludeGlobs(folderQueries: IFolderSearch[], globalInclude: glob.IExpression): IRgGlobResult { + return foldersToRgGlobs(folderQueries, fq => objects.assign({}, fq.includePattern || {}, globalInclude || {})); +} + +function foldersToRgGlobalExcludeGlobs(folderQueries: IFolderSearch[], globalExclude: glob.IExpression): IRgGlobResult { + return foldersToRgGlobs(folderQueries, () => globalExclude); +} + +function foldersToRgGlobs(folderQueries: IFolderSearch[], patternProvider: (fs: IFolderSearch) => glob.IExpression): IRgGlobResult { const globArgs: string[] = []; let siblingClauses: glob.IExpression = {}; folderQueries.forEach(folderQuery => { - const totalExcludePattern = objects.assign({}, globalExclude || {}, folderQuery.excludePattern || {}); - const result = globExprsToRgGlobs(totalExcludePattern, folderQuery.folder); + const pattern = patternProvider(folderQuery); + const result = globExprsToRgGlobs(pattern, folderQuery.folder); globArgs.push(...result.globArgs); if (result.siblingClauses) { siblingClauses = objects.assign(siblingClauses, result.siblingClauses); @@ -400,17 +413,6 @@ function foldersToRgExcludeGlobs(folderQueries: IFolderSearch[], globalExclude: return { globArgs, siblingClauses }; } -function foldersToIncludeGlobs(folderQueries: IFolderSearch[], globalInclude: glob.IExpression): string[] { - const globArgs = []; - folderQueries.forEach(folderQuery => { - const totalIncludePattern = objects.assign({}, globalInclude || {}, folderQuery.includePattern || {}); - const result = globExprsToRgGlobs(totalIncludePattern, folderQuery.folder); - globArgs.push(...result.globArgs); - }); - - return globArgs; -} - function globExprsToRgGlobs(patterns: glob.IExpression, folder: string): IRgGlobResult { const globArgs: string[] = []; let siblingClauses: glob.IExpression = null; @@ -454,16 +456,22 @@ function getRgArgs(config: IRawSearch): IRgGlobResult { const args = ['--hidden', '--heading', '--line-number', '--color', 'ansi', '--colors', 'path:none', '--colors', 'line:none', '--colors', 'match:fg:red', '--colors', 'match:style:nobold']; args.push(config.contentPattern.isCaseSensitive ? '--case-sensitive' : '--ignore-case'); - // includePattern can't have siblingClauses - foldersToIncludeGlobs(config.folderQueries, config.includePattern).forEach(globArg => { - args.push('-g', globArg); - }); + const globsToGlobArgs = (globArgs: string[]) => arrays.flatten(globArgs.map(arg => ['-g', arg])); + const globToNotGlob = (glob: string) => '!' + glob; - let siblingClauses: glob.IExpression; - const rgGlobs = foldersToRgExcludeGlobs(config.folderQueries, config.excludePattern); - rgGlobs.globArgs - .forEach(rgGlob => args.push('-g', `!${rgGlob}`)); - siblingClauses = rgGlobs.siblingClauses; + // Include/exclude precedence: + // settings exclude < global include < global exclude + const excludeResult = foldersToRgExcludeGlobs(config.folderQueries); + args.push(...globsToGlobArgs(excludeResult.globArgs.map(globToNotGlob))); + + const includeResult = foldersToRgIncludeGlobs(config.folderQueries, config.includePattern); + args.push(...globsToGlobArgs(includeResult.globArgs)); + + const globalExcludeResult = foldersToRgGlobalExcludeGlobs(config.folderQueries, config.excludePattern); + args.push(...globsToGlobArgs(globalExcludeResult.globArgs.map(globToNotGlob))); + + // includePattern can't have siblingClauses + const siblingClauses = excludeResult.siblingClauses; if (config.maxFilesize) { args.push('--max-filesize', config.maxFilesize + ''); From f21c311e8db9eff11a605b517525aa6c4dd0f09b Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Mon, 24 Jul 2017 20:46:08 -0700 Subject: [PATCH 036/136] Include/exclude precedence tests --- .../services/search/node/ripgrepTextSearch.ts | 2 +- .../test/node/textSearch.integrationTest.ts | 33 ++++++++++++++++--- 2 files changed, 30 insertions(+), 5 deletions(-) diff --git a/src/vs/workbench/services/search/node/ripgrepTextSearch.ts b/src/vs/workbench/services/search/node/ripgrepTextSearch.ts index 437c1ab2f00..2ae6c703205 100644 --- a/src/vs/workbench/services/search/node/ripgrepTextSearch.ts +++ b/src/vs/workbench/services/search/node/ripgrepTextSearch.ts @@ -402,7 +402,7 @@ function foldersToRgGlobs(folderQueries: IFolderSearch[], patternProvider: (fs: const globArgs: string[] = []; let siblingClauses: glob.IExpression = {}; folderQueries.forEach(folderQuery => { - const pattern = patternProvider(folderQuery); + const pattern = patternProvider(folderQuery) || {}; const result = globExprsToRgGlobs(pattern, folderQuery.folder); globArgs.push(...result.globArgs); if (result.siblingClauses) { diff --git a/src/vs/workbench/services/search/test/node/textSearch.integrationTest.ts b/src/vs/workbench/services/search/test/node/textSearch.integrationTest.ts index 19b60d672d5..61ae7e7cc71 100644 --- a/src/vs/workbench/services/search/test/node/textSearch.integrationTest.ts +++ b/src/vs/workbench/services/search/test/node/textSearch.integrationTest.ts @@ -172,7 +172,7 @@ suite('Search-integration', function () { const config: any = { folderQueries: ROOT_FOLDER_QUERY, contentPattern: { pattern: 'e' }, - excludePattern: { '**/examples': true } + excludePattern: makeExpression('**/examples') }; doSearchTest(config, 394, done); @@ -182,7 +182,7 @@ suite('Search-integration', function () { const config: any = { folderQueries: ROOT_FOLDER_QUERY, contentPattern: { pattern: 'e' }, - includePattern: { '**/examples/**': true } + includePattern: makeExpression('**/examples/**'), }; doSearchTest(config, 382, done); @@ -192,13 +192,24 @@ suite('Search-integration', function () { const config: any = { folderQueries: ROOT_FOLDER_QUERY, contentPattern: { pattern: 'e' }, - includePattern: { '**/examples/**': true }, - excludePattern: { '**/examples/small.js': true } + includePattern: makeExpression('**/examples/**'), + excludePattern: makeExpression('**/examples/small.js') }; doSearchTest(config, 361, done); }); + test('Text: e (include/exclude precedence)', function (done: () => void) { + const config: any = { + folderQueries: ROOT_FOLDER_QUERY, + contentPattern: { pattern: 'e' }, + includePattern: makeExpression('**/examples/**'), + excludePattern: makeExpression('**/examples/**') + }; + + doSearchTest(config, 0, done); + }); + test('Text: a (capped)', function (done: () => void) { const maxResults = 520; const config = { @@ -272,6 +283,20 @@ suite('Search-integration', function () { doSearchTest(config, 286, done); }); + + // Pending non-ripgrep precedence + // test('Multiroot: e with folder exclude precedence', function (done: () => void) { + // const config: IRawSearch = { + // folderQueries: [ + // { folder: EXAMPLES_FIXTURES, excludePattern: makeExpression('**/e*.js') }, + // { folder: MORE_FIXTURES } + // ], + // contentPattern: { pattern: 'e' }, + // includePattern: makeExpression('**/*.js') + // }; + + // doSearchTest(config, 382, done); + // }); }); function makeExpression(...patterns: string[]): glob.IExpression { From 2a1e406d8ed1a0bc90aa278241c12ae042335e89 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Tue, 25 Jul 2017 11:11:56 +0530 Subject: [PATCH 037/136] API version #1 --- src/vs/vscode.d.ts | 113 +++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 109 insertions(+), 4 deletions(-) diff --git a/src/vs/vscode.d.ts b/src/vs/vscode.d.ts index 3c04804dafa..8b6594e6229 100644 --- a/src/vs/vscode.d.ts +++ b/src/vs/vscode.d.ts @@ -2989,7 +2989,6 @@ declare module 'vscode' { */ get(section: string, defaultValue: T): T; - /** * Check if this configuration has a certain value. * @@ -3003,7 +3002,8 @@ declare module 'vscode' { * often consists of a *default* value, a global or installation-wide value, and * a workspace-specific value. The *effective* value (returned by [`get`](#WorkspaceConfiguration.get)) * is computed like this: `defaultValue` overwritten by `globalValue`, - * `globalValue` overwritten by `workspaceValue`. + * `globalValue` overwritten by `workspaceValue`. Refer to [Settings Inheritence](https://code.visualstudio.com/docs/getstarted/settings) + * for more information. * * *Note:* The configuration name must denote a leaf in the configuration tree * (`editor.fontSize` vs `editor`) otherwise no result is returned. @@ -3013,6 +3013,35 @@ declare module 'vscode' { */ inspect(section: string): { key: string; defaultValue?: T; globalValue?: T; workspaceValue?: T } | undefined; + /** + * Update the global value of the configuration + * + * *Note 1:* Setting a global value in the presence of a more specific workspace value + * has no observable effect in that workspace, but in others. Refer to [Settings Inheritence](https://code.visualstudio.com/docs/getstarted/settings) + * for more information. + * + * *Note 2:* To remove a configuration value use `undefined`, like so: `config.updateFolderValue('somekey', undefined)` + * + * @param section Configuration name, supports _dotted_ names. + * @param value The new value. + */ + updateGlobalValue(section: string, value: any): Thenable; + + /** + * Update the workspace value of the configuration + * + * *Note 1:* Setting a workspace value in the presence of a more specific folder value + * has no observable effect for the resources under respective [folder](#workspace.workspaceFolders), + * but in others. Refer to [Settings Inheritence](https://code.visualstudio.com/docs/getstarted/settings) + * for more information. + * + * *Note 2:* To remove a configuration value use `undefined`, like so: `config.updateFolderValue('somekey', undefined)` + * + * @param section Configuration name, supports _dotted_ names. + * @param value The new value. + */ + updateWorkspaceValue(section: string, value: any): Thenable; + /** * Update a configuration value. A value can be changed for the current * [workspace](#workspace.rootPath) only, or globally for all instances of the @@ -3024,6 +3053,8 @@ declare module 'vscode' { * * *Note 2:* To remove a configuration value use `undefined`, like so: `config.update('somekey', undefined)` * + * @deprecated Use [`updateGlobalValue`](#WorkspaceConfiguration.updateGlobalValue) or [`updateWorkspaceValue`](#WorkspaceConfiguration.updateWorkspaceValue) instead. + * * @param section Configuration name, supports _dotted_ names. * @param value The new value. * @param global When `true` changes the configuration value for all instances of the editor. @@ -3036,6 +3067,67 @@ declare module 'vscode' { readonly [key: string]: any; } + /** + * Represents the configuration of a resource + * + * The resource configuration is a merged view of + * + * - Default configuration + * - Global configuration + * - Workspace configuration (if available) + * - Folder configuration of the resource (if available) + * + * **Global configuration** comes from User Settings and shadows Defaults. + * + * **Workspace configuration** comes from Workspace Settings and shadows Global configuration. + * + * **Folder configurations** comes from `.vscode` folder under [workspace folders](#workspace.workspaceFolders). Each [workspace folder](#workspace.workspaceFolders) + * has a configuration and the requested resource determines which folder configuration to pick. Folder configuration shodows Workspace configuration. + * + * *Note:* Workspace and Folder configurations contains settings from `launch.json` and `tasks.json` files. Their basename will be + * part of the section identifier. The following snippets shows how to retrieve all configurations + * from `launch.json`: + * + * ```ts + * // launch.json configuration + * const config = workspace.getConfiguration(workspace.workspaceFolders[1], 'launch'); + * + * // retrieve values + * const values = config.get('configurations'); + * ``` + */ + export interface ResourceConfiguration extends WorkspaceConfiguration { + + /** + * Retrieve all information about a configuration setting. A configuration value + * often consists of a *default* value, a global or installation-wide value, + * a workspace-specific value and a folder-specific value. + * + * The *effective* value (returned by [`get`](#WorkspaceConfiguration.get)) + * is computed like this: `defaultValue` overwritten by `globalValue`, + * `globalValue` overwritten by `workspaceValue`. `workspaceValue` overwritten by `folderValue`. + * Refer to [Settings Inheritence](https://code.visualstudio.com/docs/getstarted/settings) + * for more information. + * + * *Note:* The configuration name must denote a leaf in the configuration tree + * (`editor.fontSize` vs `editor`) otherwise no result is returned. + * + * @param section Configuration name, supports _dotted_ names. + * @return Information about a configuration setting or `undefined`. + */ + inspect(section: string): { key: string; defaultValue?: T; globalValue?: T; workspaceValue?: T, folderValue?: T } | undefined; + + /** + * Update the folder value of the configuration + * + * *Note:* To remove a configuration value use `undefined`, like so: `config.updateFolderValue('somekey', undefined)` + * + * @param section Configuration name, supports _dotted_ names. + * @param value The new value. + */ + updateFolderValue(section: string, value: any): Thenable; + } + /** * Represents a location inside a resource, such as a line * inside a text file. @@ -4849,17 +4941,30 @@ declare module 'vscode' { export const onDidSaveTextDocument: Event; /** - * Get a configuration object. + * Get a workspace configuration object. * * When a section-identifier is provided only that part of the configuration * is returned. Dots in the section-identifier are interpreted as child-access, * like `{ myExt: { setting: { doIt: true }}}` and `getConfiguration('myExt.setting').get('doIt') === true`. * * @param section A dot-separated identifier. - * @return The full workspace configuration or a subset. + * @return The full configuration or a subset. */ export function getConfiguration(section?: string): WorkspaceConfiguration; + /** + * Get a resource configuration object. + * + * When a section-identifier is provided only that part of the configuration + * is returned. Dots in the section-identifier are interpreted as child-access, + * like `{ myExt: { setting: { doIt: true }}}` and `getConfiguration('myExt.setting').get('doIt') === true`. + * + * @param section A dot-separated identifier. + * @param resource A resource for which configuration is asked for + * @return The full configuration or a subset. + */ + export function getConfiguration(resource: Uri, section?: string): ResourceConfiguration; + /** * An event that is emitted when the [configuration](#WorkspaceConfiguration) changed. */ From c73d4d3476cf1d391eedae428b8cf01568209f8f Mon Sep 17 00:00:00 2001 From: Ramya Achutha Rao Date: Mon, 24 Jul 2017 23:45:54 -0700 Subject: [PATCH 038/136] Escape $ when its not a placeholder --- extensions/emmet/src/abbreviationActions.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions/emmet/src/abbreviationActions.ts b/extensions/emmet/src/abbreviationActions.ts index 36639adb39b..f5cea763b10 100644 --- a/extensions/emmet/src/abbreviationActions.ts +++ b/extensions/emmet/src/abbreviationActions.ts @@ -233,7 +233,7 @@ function expandAbbr(input: ExpandAbbreviationInput, newLine: string): string { try { expandedText = expand(input.abbreviation, expandOptions); if (input.textToWrap && input.textToWrap !== selectedTextToWrap) { - expandedText = expandedText.replace(/\$/g, '\\$'); + expandedText = expandedText.replace(/(\$[^\{])/g, '\\$&'); } } catch (e) { vscode.window.showErrorMessage('Failed to expand abbreviation'); From 887b3067e7b4e3b76b659469272dbbd2e8dfa376 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Tue, 25 Jul 2017 12:38:12 +0530 Subject: [PATCH 039/136] #31034 Register resource configurations schema --- .../common/configurationRegistry.ts | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/src/vs/platform/configuration/common/configurationRegistry.ts b/src/vs/platform/configuration/common/configurationRegistry.ts index e3a6e8d5778..7708e9a7e54 100644 --- a/src/vs/platform/configuration/common/configurationRegistry.ts +++ b/src/vs/platform/configuration/common/configurationRegistry.ts @@ -83,6 +83,7 @@ export interface IDefaultConfigurationExtension { export const schemaId = 'vscode://schemas/settings'; export const editorConfigurationSchemaId = 'vscode://schemas/settings/editor'; +export const resourceConfigurationSchemaId = 'vscode://schemas/settings/resource'; const contributionRegistry = Registry.as(JSONExtensions.JSONContribution); class ConfigurationRegistry implements IConfigurationRegistry { @@ -90,6 +91,7 @@ class ConfigurationRegistry implements IConfigurationRegistry { private configurationProperties: { [qualifiedKey: string]: IJSONSchema }; private configurationSchema: IJSONSchema; private editorConfigurationSchema: IJSONSchema; + private resourceConfigurationSchema: IJSONSchema; private _onDidRegisterConfiguration: Emitter; private overrideIdentifiers: string[] = []; private overridePropertyPattern: string; @@ -98,12 +100,14 @@ class ConfigurationRegistry implements IConfigurationRegistry { this.configurationContributors = []; this.configurationSchema = { properties: {}, patternProperties: {}, additionalProperties: false, errorMessage: 'Unknown configuration setting' }; this.editorConfigurationSchema = { properties: {}, patternProperties: {}, additionalProperties: false, errorMessage: 'Unknown editor configuration setting' }; + this.resourceConfigurationSchema = { properties: {}, patternProperties: {}, additionalProperties: false, errorMessage: 'Not a resource configuration setting' }; this._onDidRegisterConfiguration = new Emitter(); this.configurationProperties = {}; this.computeOverridePropertyPattern(); contributionRegistry.registerSchema(schemaId, this.configurationSchema); contributionRegistry.registerSchema(editorConfigurationSchemaId, this.editorConfigurationSchema); + contributionRegistry.registerSchema(resourceConfigurationSchemaId, this.resourceConfigurationSchema); } public get onDidRegisterConfiguration() { @@ -223,8 +227,9 @@ class ConfigurationRegistry implements IConfigurationRegistry { private updateSchemaForOverrideSettingsConfiguration(configuration: IConfigurationNode): void { if (configuration.id !== SETTINGS_OVERRRIDE_NODE_ID) { - this.update(configuration, this.editorConfigurationSchema); + this.update(configuration); contributionRegistry.registerSchema(editorConfigurationSchemaId, this.editorConfigurationSchema); + contributionRegistry.registerSchema(resourceConfigurationSchemaId, this.resourceConfigurationSchema); } } @@ -244,18 +249,23 @@ class ConfigurationRegistry implements IConfigurationRegistry { contributionRegistry.registerSchema(schemaId, this.configurationSchema); } - private update(configuration: IConfigurationNode, overridePropertiesSchema: IJSONSchema): void { + private update(configuration: IConfigurationNode): void { let properties = configuration.properties; if (properties) { for (let key in properties) { if (properties[key].overridable) { - overridePropertiesSchema.properties[key] = this.getConfigurationProperties()[key]; + this.editorConfigurationSchema.properties[key] = this.getConfigurationProperties()[key]; + } + switch (properties[key].scope) { + case ConfigurationScope.RESOURCE: + this.resourceConfigurationSchema.properties[key] = this.getConfigurationProperties()[key]; + break; } } } let subNodes = configuration.allOf; if (subNodes) { - subNodes.forEach(subNode => this.update(subNode, overridePropertiesSchema)); + subNodes.forEach(subNode => this.update(subNode)); } } From fed03b66ac8acdac93897727a1d19ee0f6898bf2 Mon Sep 17 00:00:00 2001 From: isidor Date: Tue, 25 Jul 2017 09:10:04 +0200 Subject: [PATCH 040/136] fixes #31364 --- .../parts/debug/electron-browser/debugConfigurationManager.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/parts/debug/electron-browser/debugConfigurationManager.ts b/src/vs/workbench/parts/debug/electron-browser/debugConfigurationManager.ts index bcd80f46839..6bd50613f98 100644 --- a/src/vs/workbench/parts/debug/electron-browser/debugConfigurationManager.ts +++ b/src/vs/workbench/parts/debug/electron-browser/debugConfigurationManager.ts @@ -229,7 +229,6 @@ export class ConfigurationManager implements IConfigurationManager { @IStorageService private storageService: IStorageService ) { this.adapters = []; - this.launches = []; this.toDispose = []; this.registerListeners(); this.initLaunches(); @@ -292,7 +291,8 @@ export class ConfigurationManager implements IConfigurationManager { } private initLaunches(): void { - this.launches = this.contextService.getWorkspace().roots.map(root => this.instantiationService.createInstance(Launch, this, root)); + const workspace = this.contextService.getWorkspace(); + this.launches = workspace ? workspace.roots.map(root => this.instantiationService.createInstance(Launch, this, root)) : []; } public getLaunches(): ILaunch[] { From ff52b7e5f23187092965fab750bfadb7d13f727f Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Tue, 25 Jul 2017 13:23:07 +0530 Subject: [PATCH 041/136] Fix #31042 --- extensions/configuration-editing/package.json | 6 +++- .../preferences/browser/preferencesEditor.ts | 2 +- .../preferences/browser/preferencesService.ts | 32 +++++++++++++++---- .../parts/preferences/common/preferences.ts | 2 -- .../preferences/common/preferencesModels.ts | 26 ++++++++++++--- 5 files changed, 54 insertions(+), 14 deletions(-) diff --git a/extensions/configuration-editing/package.json b/extensions/configuration-editing/package.json index d7fd6d3d30e..1fd34c23234 100644 --- a/extensions/configuration-editing/package.json +++ b/extensions/configuration-editing/package.json @@ -35,6 +35,10 @@ "fileMatch": "vscode://defaultsettings/settings.json", "url": "vscode://schemas/settings" }, + { + "fileMatch": "vscode://defaultsettings/resourceSettings.json", + "url": "vscode://schemas/settings/resource" + }, { "fileMatch": "vscode://settings/workspaceSettings.json", "url": "vscode://schemas/settings" @@ -80,4 +84,4 @@ "devDependencies": { "@types/node": "^7.0.4" } -} +} \ No newline at end of file diff --git a/src/vs/workbench/parts/preferences/browser/preferencesEditor.ts b/src/vs/workbench/parts/preferences/browser/preferencesEditor.ts index 340b196705b..d75bab0ad35 100644 --- a/src/vs/workbench/parts/preferences/browser/preferencesEditor.ts +++ b/src/vs/workbench/parts/preferences/browser/preferencesEditor.ts @@ -811,7 +811,7 @@ class SettingsEditorContribution extends Disposable implements ISettingsEditorCo createPreferencesRenderer(associatedPreferencesModelUri: URI): TPromise> { this.disposePreferencesRenderer(); - this.preferencesRenderer = TPromise.join([this.preferencesService.createPreferencesEditorModel(this.preferencesService.defaultSettingsResource), this.preferencesService.createPreferencesEditorModel(this.editor.getModel().uri)]) + this.preferencesRenderer = TPromise.join([this.preferencesService.createPreferencesEditorModel(associatedPreferencesModelUri), this.preferencesService.createPreferencesEditorModel(this.editor.getModel().uri)]) .then(([defaultSettingsModel, settingsModel]) => { if (settingsModel instanceof SettingsEditorModel) { switch (settingsModel.configurationTarget) { diff --git a/src/vs/workbench/parts/preferences/browser/preferencesService.ts b/src/vs/workbench/parts/preferences/browser/preferencesService.ts index 9b80dabbbe1..8fffe6ed7ce 100644 --- a/src/vs/workbench/parts/preferences/browser/preferencesService.ts +++ b/src/vs/workbench/parts/preferences/browser/preferencesService.ts @@ -40,6 +40,7 @@ import { Position, IPosition } from 'vs/editor/common/core/position'; 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 { @@ -100,8 +101,9 @@ export class PreferencesService extends Disposable implements IPreferencesServic }); } - readonly defaultSettingsResource = URI.from({ scheme: network.Schemas.vscode, authority: 'defaultsettings', path: '/settings.json' }); - readonly defaultKeybindingsResource = URI.from({ scheme: network.Schemas.vscode, authority: 'defaultsettings', path: '/keybindings.json' }); + private readonly defaultWorkbenchSettingsResource = URI.from({ scheme: network.Schemas.vscode, authority: 'defaultsettings', path: '/settings.json' }); + private readonly defaultResourceSettingsResource = URI.from({ scheme: network.Schemas.vscode, authority: 'defaultsettings', path: '/resourceSettings.json' }); + private 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 { @@ -127,11 +129,22 @@ export class PreferencesService extends Disposable implements IPreferencesServic return promise; } - if (this.defaultSettingsResource.fsPath === uri.fsPath) { + if (this.defaultWorkbenchSettingsResource.fsPath === uri.fsPath) { promise = TPromise.join([this.extensionService.onReady(), this.fetchMostCommonlyUsedSettings()]) .then(result => { const mostCommonSettings = result[1]; - const model = this.instantiationService.createInstance(DefaultSettingsEditorModel, uri, mostCommonSettings); + const model = this.instantiationService.createInstance(DefaultSettingsEditorModel, uri, mostCommonSettings, ConfigurationScope.WORKBENCH); + return model; + }); + this.defaultPreferencesEditorModels.set(uri, promise); + return promise; + } + + if (this.defaultResourceSettingsResource.fsPath === uri.fsPath) { + promise = TPromise.join([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); @@ -189,7 +202,7 @@ export class PreferencesService extends Disposable implements IPreferencesServic if (activeEditorInput instanceof PreferencesEditorInput) { return this.getOrCreateEditableSettingsEditorInput(target, this.getEditableSettingsURI(target, resource)) .then(toInput => { - const replaceWith = new PreferencesEditorInput(this.getPreferencesEditorInputName(target, resource), toInput.getDescription(), this.instantiationService.createInstance(DefaultPreferencesEditorInput, this.defaultSettingsResource), toInput); + const replaceWith = new PreferencesEditorInput(this.getPreferencesEditorInputName(target, resource), toInput.getDescription(), this.instantiationService.createInstance(DefaultPreferencesEditorInput, this.getDefaultSettingsResource(target)), toInput); return this.editorService.replaceEditors([{ toReplace: this.lastOpenedSettingsInput, replaceWith @@ -240,7 +253,7 @@ export class PreferencesService extends Disposable implements IPreferencesServic return this.getOrCreateEditableSettingsEditorInput(configurationTarget, resource) .then(editableSettingsEditorInput => { if (openDefaultSettings) { - const defaultPreferencesEditorInput = this.instantiationService.createInstance(DefaultPreferencesEditorInput, this.defaultSettingsResource); + const defaultPreferencesEditorInput = this.instantiationService.createInstance(DefaultPreferencesEditorInput, this.getDefaultSettingsResource(configurationTarget)); const preferencesEditorInput = new PreferencesEditorInput(this.getPreferencesEditorInputName(configurationTarget, resource), editableSettingsEditorInput.getDescription(), defaultPreferencesEditorInput, editableSettingsEditorInput); this.lastOpenedSettingsInput = preferencesEditorInput; return this.editorService.openEditor(preferencesEditorInput, { pinned: true }); @@ -249,6 +262,13 @@ export class PreferencesService extends Disposable implements IPreferencesServic }); } + private getDefaultSettingsResource(configurationTarget: ConfigurationTarget): URI { + if (configurationTarget === ConfigurationTarget.FOLDER) { + return this.defaultResourceSettingsResource; + } + return this.defaultWorkbenchSettingsResource; + } + 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; diff --git a/src/vs/workbench/parts/preferences/common/preferences.ts b/src/vs/workbench/parts/preferences/common/preferences.ts index 78823a00c28..e4d8c624ecc 100644 --- a/src/vs/workbench/parts/preferences/common/preferences.ts +++ b/src/vs/workbench/parts/preferences/common/preferences.ts @@ -68,10 +68,8 @@ export const IPreferencesService = createDecorator('prefere export interface IPreferencesService { _serviceBrand: any; - defaultSettingsResource: URI; userSettingsResource: URI; workspaceSettingsResource: URI; - defaultKeybindingsResource: URI; resolveContent(uri: URI): TPromise; createPreferencesEditorModel(uri: URI): TPromise>; diff --git a/src/vs/workbench/parts/preferences/common/preferencesModels.ts b/src/vs/workbench/parts/preferences/common/preferencesModels.ts index 3b5505a1b7c..785707f7093 100644 --- a/src/vs/workbench/parts/preferences/common/preferencesModels.ts +++ b/src/vs/workbench/parts/preferences/common/preferencesModels.ts @@ -15,7 +15,7 @@ import { visit, JSONVisitor } from 'vs/base/common/json'; import { IModel } from 'vs/editor/common/editorCommon'; import { IJSONSchema } from 'vs/base/common/jsonSchema'; import { EditorModel } from 'vs/workbench/common/editor'; -import { IConfigurationNode, IConfigurationRegistry, Extensions, OVERRIDE_PROPERTY_PATTERN, IConfigurationPropertySchema } from 'vs/platform/configuration/common/configurationRegistry'; +import { IConfigurationNode, IConfigurationRegistry, Extensions, OVERRIDE_PROPERTY_PATTERN, IConfigurationPropertySchema, ConfigurationScope } from 'vs/platform/configuration/common/configurationRegistry'; import { ISettingsEditorModel, IKeybindingsEditorModel, ISettingsGroup, ISetting, IFilterResult, ISettingsSection } from 'vs/workbench/parts/preferences/common/preferences'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { ConfigurationTarget } from 'vs/workbench/services/configuration/common/configurationEditing'; @@ -598,7 +598,7 @@ export class DefaultSettingsEditorModel extends AbstractSettingsModel implements private _content: string; private _contentByLines: string[]; - constructor(private _uri: URI, private _mostCommonlyUsedSettingsKeys: string[]) { + constructor(private _uri: URI, private _mostCommonlyUsedSettingsKeys: string[], private configurationScope: ConfigurationScope) { super(); } @@ -643,7 +643,7 @@ export class DefaultSettingsEditorModel extends AbstractSettingsModel implements private parse() { const configurations = Registry.as(Extensions.Configuration).getConfigurations().slice(); - const settingsGroups = configurations.sort(this.compareConfigurationNodes).reduce((result, config, index, array) => this.parseConfig(config, result, array), []); + const settingsGroups = this.removeEmptySettingsGroups(configurations.sort(this.compareConfigurationNodes).reduce((result, config, index, array) => this.parseConfig(config, result, array), [])); const mostCommonlyUsed = this.getMostCommonlyUsedSettings(settingsGroups); this._allSettingsGroups = [mostCommonlyUsed, ...settingsGroups]; this._content = this.toContent(mostCommonlyUsed, settingsGroups); @@ -721,11 +721,22 @@ export class DefaultSettingsEditorModel extends AbstractSettingsModel implements return result; } + 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; + } + private parseSettings(settingsObject: { [path: string]: IConfigurationPropertySchema; }): ISetting[] { let result = []; for (let key in settingsObject) { const prop = settingsObject[key]; - if (!prop.deprecationMessage) { + if (!prop.deprecationMessage && this.matchesScope(prop)) { const value = prop.default; const description = (prop.description || '').split('\n'); const overrides = OVERRIDE_PROPERTY_PATTERN.test(key) ? this.parseOverrideSettings(prop.default) : []; @@ -739,6 +750,13 @@ export class DefaultSettingsEditorModel extends AbstractSettingsModel implements return Object.keys(overrideSettings).map((key) => ({ key, value: overrideSettings[key], description: [], range: null, keyRange: null, valueRange: null, descriptionRanges: [], overrides: [] })); } + private matchesScope(property: IConfigurationNode): boolean { + if (this.configurationScope === ConfigurationScope.WORKBENCH) { + return true; + } + return property.scope === this.configurationScope; + } + private compareConfigurationNodes(c1: IConfigurationNode, c2: IConfigurationNode): number { if (typeof c1.order !== 'number') { return 1; From fd99653ee0054455c4e7e49bfae9eba2948dc671 Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Tue, 25 Jul 2017 11:52:50 +0200 Subject: [PATCH 042/136] fixes #31376 --- .../workbench/parts/scm/electron-browser/media/scmViewlet.css | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/vs/workbench/parts/scm/electron-browser/media/scmViewlet.css b/src/vs/workbench/parts/scm/electron-browser/media/scmViewlet.css index 1c3e3d44f5e..a55b17321cd 100644 --- a/src/vs/workbench/parts/scm/electron-browser/media/scmViewlet.css +++ b/src/vs/workbench/parts/scm/electron-browser/media/scmViewlet.css @@ -65,6 +65,8 @@ display: none; } +.scm-viewlet .monaco-list .monaco-list-row:hover > .resource-group > .actions, +.scm-viewlet .monaco-list .monaco-list-row:hover > .resource > .actions, .scm-viewlet .monaco-list .monaco-list-row.selected > .resource-group > .actions, .scm-viewlet .monaco-list .monaco-list-row.focused > .resource-group > .actions, .scm-viewlet .monaco-list .monaco-list-row.selected > .resource > .actions, From 97fd052850cf73ea7fe58f5b42937d2b010f8098 Mon Sep 17 00:00:00 2001 From: isidor Date: Tue, 25 Jul 2017 12:20:18 +0200 Subject: [PATCH 043/136] multi root configuraiton resolver service --- .../mainThreadDebugService.ts | 4 +- .../parts/debug/browser/debugActions.ts | 2 +- src/vs/workbench/parts/debug/common/debug.ts | 7 +- .../debug/electron-browser/debugCommands.ts | 4 +- .../debugConfigurationManager.ts | 8 +- .../debug/electron-browser/debugService.ts | 49 +++++----- .../debug/electron-browser/rawDebugSession.ts | 4 +- .../parts/debug/node/debugAdapter.ts | 16 ++-- .../parts/debug/test/common/mockDebug.ts | 6 +- .../debug/test/node/debugAdapter.test.ts | 4 +- .../electron-browser/task.contribution.ts | 4 +- .../electron-browser/terminalTaskSystem.ts | 5 +- .../parts/tasks/node/processRunnerDetector.ts | 8 +- .../parts/tasks/node/processTaskSystem.ts | 8 +- .../common/configurationResolver.ts | 9 +- .../node/configurationResolverService.ts | 62 ++++++------- .../node/configurationResolverService.test.ts | 91 ++++++++++--------- 17 files changed, 151 insertions(+), 140 deletions(-) diff --git a/src/vs/workbench/api/electron-browser/mainThreadDebugService.ts b/src/vs/workbench/api/electron-browser/mainThreadDebugService.ts index a1d7ac57cda..edab286ff8b 100644 --- a/src/vs/workbench/api/electron-browser/mainThreadDebugService.ts +++ b/src/vs/workbench/api/electron-browser/mainThreadDebugService.ts @@ -46,7 +46,7 @@ export class MainThreadDebugService extends MainThreadDebugServiceShape { } public $startDebugging(folderUri: URI | undefined, nameOrConfiguration: string | IConfig): TPromise { - return this.debugService.startDebugging(nameOrConfiguration).then(x => { + return this.debugService.startDebugging(folderUri, nameOrConfiguration).then(x => { return true; }, err => { return TPromise.wrapError(err && err.message ? err.message : 'cannot start debugging'); @@ -57,7 +57,7 @@ export class MainThreadDebugService extends MainThreadDebugServiceShape { if (configuration.request !== 'launch' && configuration.request !== 'attach') { return TPromise.wrapError(new Error(`only 'launch' or 'attach' allowed for 'request' attribute`)); } - return this.debugService.createProcess(configuration).then(process => { + return this.debugService.createProcess(folderUri, configuration).then(process => { if (process) { return process.getId(); } diff --git a/src/vs/workbench/parts/debug/browser/debugActions.ts b/src/vs/workbench/parts/debug/browser/debugActions.ts index 53ecce9c344..a5c7a86a91f 100644 --- a/src/vs/workbench/parts/debug/browser/debugActions.ts +++ b/src/vs/workbench/parts/debug/browser/debugActions.ts @@ -124,7 +124,7 @@ export class StartAction extends AbstractDebugAction { } public run(): TPromise { - return this.debugService.startDebugging(undefined, this.isNoDebug()); + return this.debugService.startDebugging(undefined, undefined, this.isNoDebug()); } protected isNoDebug(): boolean { diff --git a/src/vs/workbench/parts/debug/common/debug.ts b/src/vs/workbench/parts/debug/common/debug.ts index 093b30398ce..bb45ac9becc 100644 --- a/src/vs/workbench/parts/debug/common/debug.ts +++ b/src/vs/workbench/parts/debug/common/debug.ts @@ -87,6 +87,7 @@ export interface IExpression extends IReplElement, IExpressionContainer { } export interface ISession { + root: uri; stackTrace(args: DebugProtocol.StackTraceArguments): TPromise; exceptionInfo(args: DebugProtocol.ExceptionInfoArguments): TPromise; scopes(args: DebugProtocol.ScopesArguments): TPromise; @@ -385,7 +386,7 @@ export interface IConfigurationManager { selectedName: string; - selectConfiguration(launch: ILaunch, name: string): void; + selectConfiguration(launch: ILaunch, name?: string): void; getLaunches(): ILaunch[]; @@ -564,12 +565,12 @@ export interface IDebugService { * Also saves all files, manages if compounds are present in the configuration * and calls the startSessionCommand if an adapter registered it. */ - startDebugging(configOrName?: IConfig | string, noDebug?: boolean): TPromise; + startDebugging(root?: uri, configOrName?: IConfig | string, noDebug?: boolean): TPromise; /** * Creates a new debug process. Depending on the configuration will either 'launch' or 'attach'. */ - createProcess(config: IConfig): TPromise; + createProcess(root: uri, config: IConfig): TPromise; /** * Find process by ID. diff --git a/src/vs/workbench/parts/debug/electron-browser/debugCommands.ts b/src/vs/workbench/parts/debug/electron-browser/debugCommands.ts index 378881a76fd..49d609d43a4 100644 --- a/src/vs/workbench/parts/debug/electron-browser/debugCommands.ts +++ b/src/vs/workbench/parts/debug/electron-browser/debugCommands.ts @@ -32,9 +32,9 @@ export function registerCommands(): void { } if (typeof configurationOrName === 'string') { - debugService.startDebugging(configurationOrName); + debugService.startDebugging(undefined, configurationOrName); } else { - debugService.createProcess(configurationOrName); + debugService.createProcess(undefined, configurationOrName); } }, when: CONTEXT_NOT_IN_DEBUG_MODE, diff --git a/src/vs/workbench/parts/debug/electron-browser/debugConfigurationManager.ts b/src/vs/workbench/parts/debug/electron-browser/debugConfigurationManager.ts index 6bd50613f98..b8ae42b7d62 100644 --- a/src/vs/workbench/parts/debug/electron-browser/debugConfigurationManager.ts +++ b/src/vs/workbench/parts/debug/electron-browser/debugConfigurationManager.ts @@ -311,9 +311,11 @@ export class ConfigurationManager implements IConfigurationManager { return this._onDidSelectConfigurationName.event; } - public selectConfiguration(launch: ILaunch, name: string): void { + public selectConfiguration(launch: ILaunch, name?: string): void { this._selectedLaunch = launch; - this._selectedName = name; + if (name) { + this._selectedName = name; + } this.storageService.store(DEBUG_SELECTED_CONFIG_NAME_KEY, this.selectedName, StorageScope.WORKSPACE); if (launch) { this.storageService.store(DEBUG_SELECTED_ROOT, this.contextService.getRoot(launch.uri).toString(), StorageScope.WORKSPACE); @@ -449,7 +451,7 @@ class Launch implements ILaunch { // massage configuration attributes - append workspace path to relatvie paths, substitute variables in paths. Object.keys(result).forEach(key => { - result[key] = this.configurationResolverService.resolveAny(result[key]); + result[key] = this.configurationResolverService.resolveAny(this.workspaceUri, result[key]); }); const adapter = this.configurationManager.getAdapter(result.type); diff --git a/src/vs/workbench/parts/debug/electron-browser/debugService.ts b/src/vs/workbench/parts/debug/electron-browser/debugService.ts index d005eeaac70..0329bee9573 100644 --- a/src/vs/workbench/parts/debug/electron-browser/debugService.ts +++ b/src/vs/workbench/parts/debug/electron-browser/debugService.ts @@ -175,7 +175,7 @@ export class DebugService implements debug.IDebugService { this.configurationManager.selectedLaunch.resolveConfiguration(config).done(resolvedConfig => { resolvedConfig.request = 'attach'; resolvedConfig.port = broadcast.payload.port; - this.doCreateProcess(resolvedConfig, broadcast.payload.debugId); + this.doCreateProcess(this.configurationManager.selectedLaunch.workspaceUri, resolvedConfig, broadcast.payload.debugId); }, errors.onUnexpectedError); return; @@ -636,14 +636,7 @@ export class DebugService implements debug.IDebugService { this.model.removeWatchExpressions(id); } - public startDebugging(configOrName?: debug.IConfig | string, noDebug = false): TPromise { - - // temporary workaround: the folderUri should be an argument to startDebugging - let folderUri: uri = undefined; - const workspace = this.contextService.getWorkspace(); - if (workspace && workspace.roots.length > 0) { - folderUri = workspace.roots[0]; - } + public startDebugging(root?: uri, configOrName?: debug.IConfig | string, noDebug = false): TPromise { // make sure to save all files and that the configuration is up to date return this.textFileService.saveAll().then(() => this.configurationService.reloadConfiguration().then(() => @@ -653,14 +646,15 @@ export class DebugService implements debug.IDebugService { } this.launchJsonChanged = false; const manager = this.getConfigurationManager(); - let config: debug.IConfig, compound: debug.ICompound; + const launch = !root ? manager.selectedLaunch : manager.getLaunches().filter(l => l.workspaceUri.toString() === root.toString()).pop(); + let config: debug.IConfig, compound: debug.ICompound; if (!configOrName) { configOrName = this.configurationManager.selectedName; } - if (typeof configOrName === 'string' && manager.selectedLaunch) { - config = manager.selectedLaunch.getConfiguration(configOrName); - compound = manager.selectedLaunch.getCompound(configOrName); + if (typeof configOrName === 'string' && launch) { + config = launch.getConfiguration(configOrName); + compound = launch.getCompound(configOrName); } else if (typeof configOrName !== 'string') { config = configOrName; } @@ -671,7 +665,7 @@ export class DebugService implements debug.IDebugService { "Compound must have \"configurations\" attribute set in order to start multiple configurations."))); } - return TPromise.join(compound.configurations.map(name => name !== compound.name ? this.startDebugging(name) : TPromise.as(null))); + return TPromise.join(compound.configurations.map(name => name !== compound.name ? this.startDebugging(root, name) : TPromise.as(null))); } if (configOrName && !config) { return TPromise.wrapError(new Error(nls.localize('configMissing', "Configuration '{0}' is missing in 'launch.json'.", configOrName))); @@ -681,17 +675,16 @@ export class DebugService implements debug.IDebugService { if (noDebug && config) { config.noDebug = true; } - const selectedLaunch = manager.selectedLaunch; if (commandAndType && commandAndType.command) { const defaultConfig = noDebug ? { noDebug: true } : {}; - return this.commandService.executeCommand(commandAndType.command, config || defaultConfig, selectedLaunch.workspaceUri).then((result: StartSessionResult) => { - if (selectedLaunch) { + return this.commandService.executeCommand(commandAndType.command, config || defaultConfig, launch ? launch.workspaceUri : undefined).then((result: StartSessionResult) => { + if (launch) { if (result && result.status === 'initialConfiguration') { - return selectedLaunch.openConfigFile(false, commandAndType.type); + return launch.openConfigFile(false, commandAndType.type); } if (result && result.status === 'saveConfiguration') { - return this.fileService.updateContent(selectedLaunch.uri, result.content).then(() => selectedLaunch.openConfigFile(false)); + return this.fileService.updateContent(launch.uri, result.content).then(() => launch.openConfigFile(false)); } } return undefined; @@ -699,10 +692,10 @@ export class DebugService implements debug.IDebugService { } if (config) { - return this.createProcess(config); + return this.createProcess(root, config); } - if (selectedLaunch && commandAndType) { - return selectedLaunch.openConfigFile(false, commandAndType.type); + if (launch && commandAndType) { + return launch.openConfigFile(false, commandAndType.type); } return undefined; @@ -720,7 +713,7 @@ export class DebugService implements debug.IDebugService { return null; } - public createProcess(config: debug.IConfig): TPromise { + public createProcess(root: uri, config: debug.IConfig): TPromise { return this.textFileService.saveAll().then(() => (this.configurationManager.selectedLaunch ? this.configurationManager.selectedLaunch.resolveConfiguration(config) : TPromise.as(config)).then(resolvedConfig => { if (!resolvedConfig) { @@ -739,7 +732,7 @@ export class DebugService implements debug.IDebugService { const successExitCode = taskSummary && taskSummary.exitCode === 0; const failureExitCode = taskSummary && taskSummary.exitCode !== undefined && taskSummary.exitCode !== 0; if (successExitCode || (errorCount === 0 && !failureExitCode)) { - return this.doCreateProcess(resolvedConfig); + return this.doCreateProcess(root, resolvedConfig); } this.messageService.show(severity.Error, { @@ -749,7 +742,7 @@ export class DebugService implements debug.IDebugService { actions: [ new Action('debug.continue', nls.localize('debugAnyway', "Debug Anyway"), null, true, () => { this.messageService.hideAll(); - return this.doCreateProcess(resolvedConfig); + return this.doCreateProcess(root, resolvedConfig); }), this.instantiationService.createInstance(ToggleMarkersPanelAction, ToggleMarkersPanelAction.ID, ToggleMarkersPanelAction.LABEL), CloseAction @@ -780,7 +773,7 @@ export class DebugService implements debug.IDebugService { ); } - private doCreateProcess(configuration: debug.IConfig, sessionId = generateUuid()): TPromise { + private doCreateProcess(root: uri, configuration: debug.IConfig, sessionId = generateUuid()): TPromise { configuration.__sessionId = sessionId; this.allSessionIds.add(sessionId); this.updateStateAndEmit(sessionId, debug.State.Initializing); @@ -818,7 +811,7 @@ export class DebugService implements debug.IDebugService { this.customTelemetryService = new TelemetryService({ appender }, this.configurationService); } - const session = this.instantiationService.createInstance(RawDebugSession, sessionId, configuration.debugServer, adapter, this.customTelemetryService); + const session = this.instantiationService.createInstance(RawDebugSession, sessionId, configuration.debugServer, adapter, this.customTelemetryService, root); const process = this.model.addProcess(configuration, session); this.toDisposeOnSessionEnd.set(session.getId(), []); @@ -961,7 +954,7 @@ export class DebugService implements debug.IDebugService { config.noDebug = process.configuration.noDebug; } config.__restart = restartData; - this.createProcess(config).then(() => c(null), err => e(err)); + this.createProcess(process.session.root, config).then(() => c(null), err => e(err)); }, 300); }) ).then(() => { diff --git a/src/vs/workbench/parts/debug/electron-browser/rawDebugSession.ts b/src/vs/workbench/parts/debug/electron-browser/rawDebugSession.ts index 62951198d1d..40e83b2ff22 100644 --- a/src/vs/workbench/parts/debug/electron-browser/rawDebugSession.ts +++ b/src/vs/workbench/parts/debug/electron-browser/rawDebugSession.ts @@ -6,6 +6,7 @@ import nls = require('vs/nls'); import cp = require('child_process'); import net = require('net'); +import uri from 'vs/base/common/uri'; import Event, { Emitter } from 'vs/base/common/event'; import platform = require('vs/base/common/platform'); import objects = require('vs/base/common/objects'); @@ -70,6 +71,7 @@ export class RawDebugSession extends v8.V8Protocol implements debug.ISession { private debugServerPort: number, private adapter: Adapter, private customTelemetryService: ITelemetryService, + public root: uri, @IMessageService private messageService: IMessageService, @ITelemetryService private telemetryService: ITelemetryService, @IOutputService private outputService: IOutputService, @@ -434,7 +436,7 @@ export class RawDebugSession extends v8.V8Protocol implements debug.ISession { } private startServer(): TPromise { - return this.adapter.getAdapterExecutable().then(ae => this.launchServer(ae).then(() => { + return this.adapter.getAdapterExecutable(this.root).then(ae => this.launchServer(ae).then(() => { this.serverProcess.on('error', (err: Error) => this.onServerError(err)); this.serverProcess.on('exit', (code: number, signal: string) => this.onServerExit()); diff --git a/src/vs/workbench/parts/debug/node/debugAdapter.ts b/src/vs/workbench/parts/debug/node/debugAdapter.ts index 944fd5b5227..9b656fbd65f 100644 --- a/src/vs/workbench/parts/debug/node/debugAdapter.ts +++ b/src/vs/workbench/parts/debug/node/debugAdapter.ts @@ -31,19 +31,19 @@ export class Adapter { } } - public getAdapterExecutable(verifyAgainstFS = true): TPromise { + public getAdapterExecutable(root: uri, verifyAgainstFS = true): TPromise { if (this.rawAdapter.adapterExecutableCommand) { - return this.commandService.executeCommand(this.rawAdapter.adapterExecutableCommand).then(ad => { + return this.commandService.executeCommand(this.rawAdapter.adapterExecutableCommand, root.toString()).then(ad => { return this.verifyAdapterDetails(ad, verifyAgainstFS); }); } const adapterExecutable = { - command: this.getProgram(), + command: this.getProgram(root), args: this.getAttributeBasedOnPlatform('args') }; - const runtime = this.getRuntime(); + const runtime = this.getRuntime(root); if (runtime) { const runtimeArgs = this.getAttributeBasedOnPlatform('runtimeArgs'); adapterExecutable.args = (runtimeArgs || []).concat([adapterExecutable.command]).concat(adapterExecutable.args || []); @@ -82,19 +82,19 @@ export class Adapter { "Cannot determine executable for debug adapter '{0}'.", details.command))); } - private getRuntime(): string { + private getRuntime(root: uri): string { let runtime = this.getAttributeBasedOnPlatform('runtime'); if (runtime && runtime.indexOf('./') === 0) { - runtime = this.configurationResolverService ? this.configurationResolverService.resolve(runtime) : runtime; + runtime = this.configurationResolverService ? this.configurationResolverService.resolve(root, runtime) : runtime; runtime = paths.join(this.extensionDescription.extensionFolderPath, runtime); } return runtime; } - private getProgram(): string { + private getProgram(root: uri): string { let program = this.getAttributeBasedOnPlatform('program'); if (program) { - program = this.configurationResolverService ? this.configurationResolverService.resolve(program) : program; + program = this.configurationResolverService ? this.configurationResolverService.resolve(root, program) : program; program = paths.join(this.extensionDescription.extensionFolderPath, program); } return program; diff --git a/src/vs/workbench/parts/debug/test/common/mockDebug.ts b/src/vs/workbench/parts/debug/test/common/mockDebug.ts index a7e529ff329..d2b761947a0 100644 --- a/src/vs/workbench/parts/debug/test/common/mockDebug.ts +++ b/src/vs/workbench/parts/debug/test/common/mockDebug.ts @@ -83,11 +83,11 @@ export class MockDebugService implements debug.IDebugService { public removeWatchExpressions(id?: string): void { } - public startDebugging(configOrName?: debug.IConfig | string, noDebug?: boolean): TPromise { + public startDebugging(root: uri, configOrName?: debug.IConfig | string, noDebug?: boolean): TPromise { return TPromise.as(null); } - public createProcess(config: debug.IConfig): TPromise { + public createProcess(root: uri, config: debug.IConfig): TPromise { return TPromise.as(null); } @@ -124,6 +124,8 @@ export class MockSession implements debug.ISession { return 'mockrawsession'; } + public root: uri; + public getLengthInSeconds(): number { return 100; } diff --git a/src/vs/workbench/parts/debug/test/node/debugAdapter.test.ts b/src/vs/workbench/parts/debug/test/node/debugAdapter.test.ts index 420f398feaa..0bf502a6008 100644 --- a/src/vs/workbench/parts/debug/test/node/debugAdapter.test.ts +++ b/src/vs/workbench/parts/debug/test/node/debugAdapter.test.ts @@ -55,7 +55,7 @@ suite('Debug - Adapter', () => { assert.equal(adapter.type, rawAdapter.type); assert.equal(adapter.label, rawAdapter.label); - return adapter.getAdapterExecutable(false).then(details => { + return adapter.getAdapterExecutable(undefined, false).then(details => { assert.equal(details.command, paths.join(extensionFolderPath, rawAdapter.program)); assert.deepEqual(details.args, rawAdapter.args); }); @@ -103,7 +103,7 @@ suite('Debug - Adapter', () => { engines: null }); - return adapter.getAdapterExecutable(false).then(details => { + return adapter.getAdapterExecutable(undefined, false).then(details => { assert.equal(details.command, platform.isLinux ? da.linux.runtime : platform.isMacintosh ? da.osx.runtime : da.win.runtime); assert.deepEqual(details.args, da.runtimeArgs.concat(['a/b/c/d/mockprogram'].concat(da.args))); }); diff --git a/src/vs/workbench/parts/tasks/electron-browser/task.contribution.ts b/src/vs/workbench/parts/tasks/electron-browser/task.contribution.ts index bf8da6b58a5..4ea0efcddba 100644 --- a/src/vs/workbench/parts/tasks/electron-browser/task.contribution.ts +++ b/src/vs/workbench/parts/tasks/electron-browser/task.contribution.ts @@ -1257,13 +1257,13 @@ class TaskService extends EventEmitter implements ITaskService { this._taskSystem = new TerminalTaskSystem( this.terminalService, this.outputService, this.markerService, this.modelService, this.configurationResolverService, this.telemetryService, - this.workbenchEditorService, + this.workbenchEditorService, this.contextService, TaskService.OutputChannelId ); } else { let system = new ProcessTaskSystem( this.markerService, this.modelService, this.telemetryService, this.outputService, - this.configurationResolverService, TaskService.OutputChannelId, + this.configurationResolverService, this.contextService, TaskService.OutputChannelId, ); system.hasErrors(this._configHasErrors); this._taskSystem = system; diff --git a/src/vs/workbench/parts/tasks/electron-browser/terminalTaskSystem.ts b/src/vs/workbench/parts/tasks/electron-browser/terminalTaskSystem.ts index 3ad7ca39063..9b3aaa6c15b 100644 --- a/src/vs/workbench/parts/tasks/electron-browser/terminalTaskSystem.ts +++ b/src/vs/workbench/parts/tasks/electron-browser/terminalTaskSystem.ts @@ -23,6 +23,7 @@ import * as TPath from 'vs/base/common/paths'; // import URI from 'vs/base/common/uri'; import { IMarkerService } from 'vs/platform/markers/common/markers'; +import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; import { IModelService } from 'vs/editor/common/services/modelService'; import { ProblemMatcher, ProblemMatcherRegistry /*, ProblemPattern, getResource */ } from 'vs/platform/markers/common/problemMatcher'; @@ -117,6 +118,7 @@ export class TerminalTaskSystem extends EventEmitter implements ITaskSystem { private configurationResolverService: IConfigurationResolverService, private telemetryService: ITelemetryService, private workbenchEditorService: IWorkbenchEditorService, + private contextService: IWorkspaceContextService, outputChannelId: string) { super(); @@ -631,7 +633,8 @@ export class TerminalTaskSystem extends EventEmitter implements ITaskSystem { } private resolveVariable(value: string): string { - return this.configurationResolverService.resolve(value); + // TODO@Dirk adopt new configuration resolver service https://github.com/Microsoft/vscode/issues/31365 + return this.configurationResolverService.resolve(this.contextService.getLegacyWorkspace().resource, value); } private resolveOptions(options: CommandOptions): CommandOptions { diff --git a/src/vs/workbench/parts/tasks/node/processRunnerDetector.ts b/src/vs/workbench/parts/tasks/node/processRunnerDetector.ts index dcf646daff4..ed09f3635ba 100644 --- a/src/vs/workbench/parts/tasks/node/processRunnerDetector.ts +++ b/src/vs/workbench/parts/tasks/node/processRunnerDetector.ts @@ -173,8 +173,9 @@ export class ProcessRunnerDetector { let args = (this.taskConfiguration.args || []).concat(config.arg); let options: CommandOptions = this.taskConfiguration.options ? this.resolveCommandOptions(this.taskConfiguration.options) : { cwd: this._cwd }; let isShellCommand = !!this.taskConfiguration.isShellCommand; + // TODO@Dirk adopt new configuration resolver service https://github.com/Microsoft/vscode/issues/31365 return this.runDetection( - new LineProcess(this.taskConfiguration.command, this.configurationResolverService.resolve(args), isShellCommand, options), + new LineProcess(this.taskConfiguration.command, this.configurationResolverService.resolve(this.contextService.getLegacyWorkspace().resource, args), isShellCommand, options), this.taskConfiguration.command, isShellCommand, config.matcher, ProcessRunnerDetector.DefaultProblemMatchers, list); } else { if (detectSpecific) { @@ -215,12 +216,13 @@ export class ProcessRunnerDetector { } private resolveCommandOptions(options: CommandOptions): CommandOptions { + // TODO@Dirk adopt new configuration resolver service https://github.com/Microsoft/vscode/issues/31365 let result = Objects.clone(options); if (result.cwd) { - result.cwd = this.configurationResolverService.resolve(result.cwd); + result.cwd = this.configurationResolverService.resolve(this.contextService.getLegacyWorkspace().resource, result.cwd); } if (result.env) { - result.env = this.configurationResolverService.resolve(result.env); + result.env = this.configurationResolverService.resolve(this.contextService.getLegacyWorkspace().resource, result.env); } return result; } diff --git a/src/vs/workbench/parts/tasks/node/processTaskSystem.ts b/src/vs/workbench/parts/tasks/node/processTaskSystem.ts index ed4a91d2d4e..55556c6f24c 100644 --- a/src/vs/workbench/parts/tasks/node/processTaskSystem.ts +++ b/src/vs/workbench/parts/tasks/node/processTaskSystem.ts @@ -24,6 +24,7 @@ import { IMarkerService } from 'vs/platform/markers/common/markers'; import { IModelService } from 'vs/editor/common/services/modelService'; import { ProblemMatcher, ProblemMatcherRegistry } from 'vs/platform/markers/common/problemMatcher'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; import { StartStopProblemCollector, WatchingProblemCollector, ProblemCollectorEvents } from 'vs/workbench/parts/tasks/common/problemCollectors'; import { @@ -43,6 +44,7 @@ export class ProcessTaskSystem extends EventEmitter implements ITaskSystem { private outputService: IOutputService; private telemetryService: ITelemetryService; private configurationResolverService: IConfigurationResolverService; + private contextService: IWorkspaceContextService; private outputChannel: IOutputChannel; @@ -52,11 +54,12 @@ export class ProcessTaskSystem extends EventEmitter implements ITaskSystem { private activeTaskPromise: TPromise; constructor(markerService: IMarkerService, modelService: IModelService, telemetryService: ITelemetryService, - outputService: IOutputService, configurationResolverService: IConfigurationResolverService, outputChannelId: string) { + outputService: IOutputService, configurationResolverService: IConfigurationResolverService, contextService: IWorkspaceContextService, outputChannelId: string) { super(); this.markerService = markerService; this.modelService = modelService; this.outputService = outputService; + this.contextService = contextService; this.telemetryService = telemetryService; this.configurationResolverService = configurationResolverService; @@ -374,7 +377,8 @@ export class ProcessTaskSystem extends EventEmitter implements ITaskSystem { } private resolveVariable(value: string): string { - return this.configurationResolverService.resolve(value); + // TODO@Dirk adopt new configuration resolver service https://github.com/Microsoft/vscode/issues/31365 + return this.configurationResolverService.resolve(this.contextService.getLegacyWorkspace().resource, value); } public log(value: string): void { diff --git a/src/vs/workbench/services/configurationResolver/common/configurationResolver.ts b/src/vs/workbench/services/configurationResolver/common/configurationResolver.ts index 90080c38cd9..a4d5f33a9ff 100644 --- a/src/vs/workbench/services/configurationResolver/common/configurationResolver.ts +++ b/src/vs/workbench/services/configurationResolver/common/configurationResolver.ts @@ -3,6 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import uri from 'vs/base/common/uri'; import { TPromise } from 'vs/base/common/winjs.base'; import { IStringDictionary } from 'vs/base/common/collections'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; @@ -13,9 +14,9 @@ export interface IConfigurationResolverService { _serviceBrand: any; // TODO@Isidor improve this API - resolve(value: string): string; - resolve(value: string[]): string[]; - resolve(value: IStringDictionary): IStringDictionary; - resolveAny(value: T): T; + resolve(root: uri, value: string): string; + resolve(root: uri, value: string[]): string[]; + resolve(root: uri, value: IStringDictionary): IStringDictionary; + resolveAny(root: uri, value: T): T; resolveInteractiveVariables(configuration: any, interactiveVariablesMap: { [key: string]: string }): TPromise; } diff --git a/src/vs/workbench/services/configurationResolver/node/configurationResolverService.ts b/src/vs/workbench/services/configurationResolver/node/configurationResolverService.ts index 6cc318a718e..e65a6cd14a4 100644 --- a/src/vs/workbench/services/configurationResolver/node/configurationResolverService.ts +++ b/src/vs/workbench/services/configurationResolver/node/configurationResolverService.ts @@ -3,6 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import uri from 'vs/base/common/uri'; import * as paths from 'vs/base/common/paths'; import * as types from 'vs/base/common/types'; import { TPromise } from 'vs/base/common/winjs.base'; @@ -15,12 +16,11 @@ import { ICommandService } from 'vs/platform/commands/common/commands'; import { ICommonCodeEditor } from 'vs/editor/common/editorCommon'; import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService'; import { toResource } from 'vs/workbench/common/editor'; -import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; export class ConfigurationResolverService implements IConfigurationResolverService { _serviceBrand: any; - private _workspaceRoot: string; private _execPath: string; + private _workspaceRoot: string; constructor( envVariables: { [key: string]: string }, @@ -28,9 +28,7 @@ export class ConfigurationResolverService implements IConfigurationResolverServi @IEnvironmentService environmentService: IEnvironmentService, @IConfigurationService private configurationService: IConfigurationService, @ICommandService private commandService: ICommandService, - @IWorkspaceContextService private contextService: IWorkspaceContextService ) { - this._workspaceRoot = paths.normalize(contextService.hasWorkspace() ? contextService.getLegacyWorkspace().resource.fsPath : '', true); // TODO@Isidor (https://github.com/Microsoft/vscode/issues/29246) this._execPath = environmentService.execPath; Object.keys(envVariables).forEach(key => { this[`env:${key}`] = envVariables[key]; @@ -103,35 +101,37 @@ export class ConfigurationResolverService implements IConfigurationResolverServi return paths.normalize(fileResource.fsPath, true); } - public resolve(value: string): string; - public resolve(value: string[]): string[]; - public resolve(value: IStringDictionary): IStringDictionary; - public resolve(value: any): any { + public resolve(root: uri, value: string): string; + public resolve(root: uri, value: string[]): string[]; + public resolve(root: uri, value: IStringDictionary): IStringDictionary; + public resolve(root: uri, value: any): any { + this._workspaceRoot = root.fsPath.toString(); if (types.isString(value)) { - return this.resolveString(value); + return this.resolveString(root, value); } else if (types.isArray(value)) { - return this.resolveArray(value); + return this.resolveArray(root, value); } else if (types.isObject(value)) { - return this.resolveLiteral(value); + return this.resolveLiteral(root, value); } return value; } - public resolveAny(value: T): T; - public resolveAny(value: any): any { + public resolveAny(root: uri, value: T): T; + public resolveAny(root: uri, value: any): any { + this._workspaceRoot = root.fsPath.toString(); if (types.isString(value)) { - return this.resolveString(value); + return this.resolveString(root, value); } else if (types.isArray(value)) { - return this.resolveAnyArray(value); + return this.resolveAnyArray(root, value); } else if (types.isObject(value)) { - return this.resolveAnyLiteral(value); + return this.resolveAnyLiteral(root, value); } return value; } - private resolveString(value: string): string { + private resolveString(root: uri, value: string): string { let regexp = /\$\{(.*?)\}/g; const originalValue = value; const resolvedString = value.replace(regexp, (match: string, name: string) => { @@ -143,10 +143,10 @@ export class ConfigurationResolverService implements IConfigurationResolverServi } }); - return this.resolveConfigVariable(resolvedString, originalValue); + return this.resolveConfigVariable(root, resolvedString, originalValue); } - private resolveConfigVariable(value: string, originalValue: string): string { + private resolveConfigVariable(root: uri, value: string, originalValue: string): string { const replacer = (match: string, name: string) => { let config = this.configurationService.getConfiguration(); let newValue: any; @@ -168,41 +168,41 @@ export class ConfigurationResolverService implements IConfigurationResolverServi } if (types.isString(newValue)) { // Prevent infinite recursion and also support nested references (or tokens) - return newValue === originalValue ? '' : this.resolveString(newValue); + return newValue === originalValue ? '' : this.resolveString(root, newValue); } else { - return this.resolve(newValue) + ''; + return this.resolve(root, newValue) + ''; } }; return value.replace(/\$\{config:(.+?)\}/g, replacer); } - private resolveLiteral(values: IStringDictionary | string[]>): IStringDictionary | string[]> { + private resolveLiteral(root: uri, values: IStringDictionary | string[]>): IStringDictionary | string[]> { let result: IStringDictionary | string[]> = Object.create(null); Object.keys(values).forEach(key => { let value = values[key]; - result[key] = this.resolve(value); + result[key] = this.resolve(root, value); }); return result; } - private resolveAnyLiteral(values: T): T; - private resolveAnyLiteral(values: any): any { + private resolveAnyLiteral(root: uri, values: T): T; + private resolveAnyLiteral(root: uri, values: any): any { let result: IStringDictionary | string[]> = Object.create(null); Object.keys(values).forEach(key => { let value = values[key]; - result[key] = this.resolveAny(value); + result[key] = this.resolveAny(root, value); }); return result; } - private resolveArray(value: string[]): string[] { - return value.map(s => this.resolveString(s)); + private resolveArray(root: uri, value: string[]): string[] { + return value.map(s => this.resolveString(root, s)); } - private resolveAnyArray(value: T[]): T[]; - private resolveAnyArray(value: any[]): any[] { - return value.map(s => this.resolveAny(s)); + private resolveAnyArray(root: uri, value: T[]): T[]; + private resolveAnyArray(root: uri, value: any[]): any[] { + return value.map(s => this.resolveAny(root, s)); } /** diff --git a/src/vs/workbench/services/configurationResolver/test/node/configurationResolverService.test.ts b/src/vs/workbench/services/configurationResolver/test/node/configurationResolverService.test.ts index f0b67873917..12fc724a449 100644 --- a/src/vs/workbench/services/configurationResolver/test/node/configurationResolverService.test.ts +++ b/src/vs/workbench/services/configurationResolver/test/node/configurationResolverService.test.ts @@ -11,21 +11,22 @@ import { IConfigurationService, getConfigurationValue, IConfigurationOverrides, import { ICommandService } from 'vs/platform/commands/common/commands'; import { IConfigurationResolverService } from 'vs/workbench/services/configurationResolver/common/configurationResolver'; import { ConfigurationResolverService } from 'vs/workbench/services/configurationResolver/node/configurationResolverService'; -import { TestEnvironmentService, TestEditorService, TestContextService } from 'vs/workbench/test/workbenchTestServices'; +import { TestEnvironmentService, TestEditorService } from 'vs/workbench/test/workbenchTestServices'; import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService'; -import { testWorkspace } from 'vs/platform/workspace/test/common/testWorkspace'; suite('Configuration Resolver Service', () => { let configurationResolverService: IConfigurationResolverService; let envVariables: { [key: string]: string } = { key1: 'Value for Key1', key2: 'Value for Key2' }; let mockCommandService: MockCommandService; let editorService: TestEditorService; + let workspaceUri: uri; setup(() => { mockCommandService = new MockCommandService(); editorService = new TestEditorService(); - configurationResolverService = new ConfigurationResolverService(envVariables, editorService, TestEnvironmentService, new TestConfigurationService(), mockCommandService, new TestContextService(testWorkspace(uri.parse('file:///VSCode/workspaceLocation')))); + workspaceUri = uri.parse('file:///VSCode/workspaceLocation'); + configurationResolverService = new ConfigurationResolverService(envVariables, editorService, TestEnvironmentService, new TestConfigurationService(), mockCommandService); }); teardown(() => { @@ -35,41 +36,41 @@ suite('Configuration Resolver Service', () => { test('substitute one', () => { if (platform.isWindows) { - assert.strictEqual(configurationResolverService.resolve('abc ${workspaceRoot} xyz'), 'abc \\VSCode\\workspaceLocation xyz'); + assert.strictEqual(configurationResolverService.resolve(workspaceUri, 'abc ${workspaceRoot} xyz'), 'abc \\VSCode\\workspaceLocation xyz'); } else { - assert.strictEqual(configurationResolverService.resolve('abc ${workspaceRoot} xyz'), 'abc /VSCode/workspaceLocation xyz'); + assert.strictEqual(configurationResolverService.resolve(workspaceUri, 'abc ${workspaceRoot} xyz'), 'abc /VSCode/workspaceLocation xyz'); } }); test('workspace root folder name', () => { - assert.strictEqual(configurationResolverService.resolve('abc ${workspaceRootFolderName} xyz'), 'abc workspaceLocation xyz'); + assert.strictEqual(configurationResolverService.resolve(workspaceUri, 'abc ${workspaceRootFolderName} xyz'), 'abc workspaceLocation xyz'); }); test('current selected line number', () => { - assert.strictEqual(configurationResolverService.resolve('abc ${lineNumber} xyz'), `abc ${editorService.mockLineNumber} xyz`); + assert.strictEqual(configurationResolverService.resolve(workspaceUri, 'abc ${lineNumber} xyz'), `abc ${editorService.mockLineNumber} xyz`); }); test('substitute many', () => { if (platform.isWindows) { - assert.strictEqual(configurationResolverService.resolve('${workspaceRoot} - ${workspaceRoot}'), '\\VSCode\\workspaceLocation - \\VSCode\\workspaceLocation'); + assert.strictEqual(configurationResolverService.resolve(workspaceUri, '${workspaceRoot} - ${workspaceRoot}'), '\\VSCode\\workspaceLocation - \\VSCode\\workspaceLocation'); } else { - assert.strictEqual(configurationResolverService.resolve('${workspaceRoot} - ${workspaceRoot}'), '/VSCode/workspaceLocation - /VSCode/workspaceLocation'); + assert.strictEqual(configurationResolverService.resolve(workspaceUri, '${workspaceRoot} - ${workspaceRoot}'), '/VSCode/workspaceLocation - /VSCode/workspaceLocation'); } }); test('substitute one env variable', () => { if (platform.isWindows) { - assert.strictEqual(configurationResolverService.resolve('abc ${workspaceRoot} ${env:key1} xyz'), 'abc \\VSCode\\workspaceLocation Value for Key1 xyz'); + assert.strictEqual(configurationResolverService.resolve(workspaceUri, 'abc ${workspaceRoot} ${env:key1} xyz'), 'abc \\VSCode\\workspaceLocation Value for Key1 xyz'); } else { - assert.strictEqual(configurationResolverService.resolve('abc ${workspaceRoot} ${env:key1} xyz'), 'abc /VSCode/workspaceLocation Value for Key1 xyz'); + assert.strictEqual(configurationResolverService.resolve(workspaceUri, 'abc ${workspaceRoot} ${env:key1} xyz'), 'abc /VSCode/workspaceLocation Value for Key1 xyz'); } }); test('substitute many env variable', () => { if (platform.isWindows) { - assert.strictEqual(configurationResolverService.resolve('${workspaceRoot} - ${workspaceRoot} ${env:key1} - ${env:key2}'), '\\VSCode\\workspaceLocation - \\VSCode\\workspaceLocation Value for Key1 - Value for Key2'); + assert.strictEqual(configurationResolverService.resolve(workspaceUri, '${workspaceRoot} - ${workspaceRoot} ${env:key1} - ${env:key2}'), '\\VSCode\\workspaceLocation - \\VSCode\\workspaceLocation Value for Key1 - Value for Key2'); } else { - assert.strictEqual(configurationResolverService.resolve('${workspaceRoot} - ${workspaceRoot} ${env:key1} - ${env:key2}'), '/VSCode/workspaceLocation - /VSCode/workspaceLocation Value for Key1 - Value for Key2'); + assert.strictEqual(configurationResolverService.resolve(workspaceUri, '${workspaceRoot} - ${workspaceRoot} ${env:key1} - ${env:key2}'), '/VSCode/workspaceLocation - /VSCode/workspaceLocation Value for Key1 - Value for Key2'); } }); @@ -86,8 +87,8 @@ suite('Configuration Resolver Service', () => { } }); - let service = new ConfigurationResolverService(envVariables, new TestEditorService(), TestEnvironmentService, configurationService, mockCommandService, new TestContextService(testWorkspace(uri.parse('file:///VSCode/workspaceLocation')))); - assert.strictEqual(service.resolve('abc ${config:editor.fontFamily} xyz'), 'abc foo xyz'); + let service = new ConfigurationResolverService(envVariables, new TestEditorService(), TestEnvironmentService, configurationService, mockCommandService); + assert.strictEqual(service.resolve(workspaceUri, 'abc ${config:editor.fontFamily} xyz'), 'abc foo xyz'); }); test('substitute many configuration variables', () => { @@ -103,8 +104,8 @@ suite('Configuration Resolver Service', () => { } }); - let service = new ConfigurationResolverService(envVariables, new TestEditorService(), TestEnvironmentService, configurationService, mockCommandService, new TestContextService(testWorkspace(uri.parse('file:///VSCode/workspaceLocation')))); - assert.strictEqual(service.resolve('abc ${config:editor.fontFamily} ${config:terminal.integrated.fontFamily} xyz'), 'abc foo bar xyz'); + let service = new ConfigurationResolverService(envVariables, new TestEditorService(), TestEnvironmentService, configurationService, mockCommandService); + assert.strictEqual(service.resolve(workspaceUri, 'abc ${config:editor.fontFamily} ${config:terminal.integrated.fontFamily} xyz'), 'abc foo bar xyz'); }); test('substitute nested configuration variables', () => { @@ -120,11 +121,11 @@ suite('Configuration Resolver Service', () => { } }); - let service = new ConfigurationResolverService(envVariables, new TestEditorService(), TestEnvironmentService, configurationService, mockCommandService, new TestContextService(testWorkspace(uri.parse('file:///VSCode/workspaceLocation')))); + let service = new ConfigurationResolverService(envVariables, new TestEditorService(), TestEnvironmentService, configurationService, mockCommandService); if (platform.isWindows) { - assert.strictEqual(service.resolve('abc ${config:editor.fontFamily} ${config:terminal.integrated.fontFamily} xyz'), 'abc foo \\VSCode\\workspaceLocation bar bar xyz'); + assert.strictEqual(service.resolve(workspaceUri, 'abc ${config:editor.fontFamily} ${config:terminal.integrated.fontFamily} xyz'), 'abc foo \\VSCode\\workspaceLocation bar bar xyz'); } else { - assert.strictEqual(service.resolve('abc ${config:editor.fontFamily} ${config:terminal.integrated.fontFamily} xyz'), 'abc foo /VSCode/workspaceLocation bar bar xyz'); + assert.strictEqual(service.resolve(workspaceUri, 'abc ${config:editor.fontFamily} ${config:terminal.integrated.fontFamily} xyz'), 'abc foo /VSCode/workspaceLocation bar bar xyz'); } }); @@ -141,11 +142,11 @@ suite('Configuration Resolver Service', () => { } }); - let service = new ConfigurationResolverService(envVariables, new TestEditorService(), TestEnvironmentService, configurationService, mockCommandService, new TestContextService(testWorkspace(uri.parse('file:///VSCode/workspaceLocation')))); + let service = new ConfigurationResolverService(envVariables, new TestEditorService(), TestEnvironmentService, configurationService, mockCommandService); if (platform.isWindows) { - assert.strictEqual(service.resolve('abc ${config:editor.fontFamily} ${config:terminal.integrated.fontFamily} xyz'), 'abc foo \\VSCode\\workspaceLocation bar bar xyz'); + assert.strictEqual(service.resolve(workspaceUri, 'abc ${config:editor.fontFamily} ${config:terminal.integrated.fontFamily} xyz'), 'abc foo \\VSCode\\workspaceLocation bar bar xyz'); } else { - assert.strictEqual(service.resolve('abc ${config:editor.fontFamily} ${config:terminal.integrated.fontFamily} xyz'), 'abc foo /VSCode/workspaceLocation bar bar xyz'); + assert.strictEqual(service.resolve(workspaceUri, 'abc ${config:editor.fontFamily} ${config:terminal.integrated.fontFamily} xyz'), 'abc foo /VSCode/workspaceLocation bar bar xyz'); } }); @@ -162,11 +163,11 @@ suite('Configuration Resolver Service', () => { } }); - let service = new ConfigurationResolverService(envVariables, new TestEditorService(), TestEnvironmentService, configurationService, mockCommandService, new TestContextService(testWorkspace(uri.parse('file:///VSCode/workspaceLocation')))); + let service = new ConfigurationResolverService(envVariables, new TestEditorService(), TestEnvironmentService, configurationService, mockCommandService); if (platform.isWindows) { - assert.strictEqual(service.resolve('abc ${config:editor.fontFamily} ${workspaceRoot} ${env:key1} xyz'), 'abc foo \\VSCode\\workspaceLocation Value for Key1 xyz'); + assert.strictEqual(service.resolve(workspaceUri, 'abc ${config:editor.fontFamily} ${workspaceRoot} ${env:key1} xyz'), 'abc foo \\VSCode\\workspaceLocation Value for Key1 xyz'); } else { - assert.strictEqual(service.resolve('abc ${config:editor.fontFamily} ${workspaceRoot} ${env:key1} xyz'), 'abc foo /VSCode/workspaceLocation Value for Key1 xyz'); + assert.strictEqual(service.resolve(workspaceUri, 'abc ${config:editor.fontFamily} ${workspaceRoot} ${env:key1} xyz'), 'abc foo /VSCode/workspaceLocation Value for Key1 xyz'); } }); @@ -183,11 +184,11 @@ suite('Configuration Resolver Service', () => { } }); - let service = new ConfigurationResolverService(envVariables, new TestEditorService(), TestEnvironmentService, configurationService, mockCommandService, new TestContextService(testWorkspace(uri.parse('file:///VSCode/workspaceLocation')))); + let service = new ConfigurationResolverService(envVariables, new TestEditorService(), TestEnvironmentService, configurationService, mockCommandService); if (platform.isWindows) { - assert.strictEqual(service.resolve('${config:editor.fontFamily} ${config:terminal.integrated.fontFamily} ${workspaceRoot} - ${workspaceRoot} ${env:key1} - ${env:key2}'), 'foo bar \\VSCode\\workspaceLocation - \\VSCode\\workspaceLocation Value for Key1 - Value for Key2'); + assert.strictEqual(service.resolve(workspaceUri, '${config:editor.fontFamily} ${config:terminal.integrated.fontFamily} ${workspaceRoot} - ${workspaceRoot} ${env:key1} - ${env:key2}'), 'foo bar \\VSCode\\workspaceLocation - \\VSCode\\workspaceLocation Value for Key1 - Value for Key2'); } else { - assert.strictEqual(service.resolve('${config:editor.fontFamily} ${config:terminal.integrated.fontFamily} ${workspaceRoot} - ${workspaceRoot} ${env:key1} - ${env:key2}'), 'foo bar /VSCode/workspaceLocation - /VSCode/workspaceLocation Value for Key1 - Value for Key2'); + assert.strictEqual(service.resolve(workspaceUri, '${config:editor.fontFamily} ${config:terminal.integrated.fontFamily} ${workspaceRoot} - ${workspaceRoot} ${env:key1} - ${env:key2}'), 'foo bar /VSCode/workspaceLocation - /VSCode/workspaceLocation Value for Key1 - Value for Key2'); } }); @@ -217,8 +218,8 @@ suite('Configuration Resolver Service', () => { } }); - let service = new ConfigurationResolverService(envVariables, new TestEditorService(), TestEnvironmentService, configurationService, mockCommandService, new TestContextService(testWorkspace(uri.parse('file:///VSCode/workspaceLocation')))); - assert.strictEqual(service.resolve('abc ${config:editor.fontFamily} ${config:editor.lineNumbers} ${config:editor.insertSpaces} xyz'), 'abc foo 123 false xyz'); + let service = new ConfigurationResolverService(envVariables, new TestEditorService(), TestEnvironmentService, configurationService, mockCommandService); + assert.strictEqual(service.resolve(workspaceUri, 'abc ${config:editor.fontFamily} ${config:editor.lineNumbers} ${config:editor.insertSpaces} xyz'), 'abc foo 123 false xyz'); }); test('configuration should not evaluate Javascript', () => { @@ -229,8 +230,8 @@ suite('Configuration Resolver Service', () => { } }); - let service = new ConfigurationResolverService(envVariables, new TestEditorService(), TestEnvironmentService, configurationService, mockCommandService, new TestContextService(testWorkspace(uri.parse('file:///VSCode/workspaceLocation')))); - assert.strictEqual(service.resolve('abc ${config:editor[\'abc\'.substr(0)]} xyz'), 'abc xyz'); + let service = new ConfigurationResolverService(envVariables, new TestEditorService(), TestEnvironmentService, configurationService, mockCommandService); + assert.strictEqual(service.resolve(workspaceUri, 'abc ${config:editor[\'abc\'.substr(0)]} xyz'), 'abc xyz'); }); test('uses empty string as fallback', () => { @@ -239,11 +240,11 @@ suite('Configuration Resolver Service', () => { editor: {} }); - let service = new ConfigurationResolverService(envVariables, new TestEditorService(), TestEnvironmentService, configurationService, mockCommandService, new TestContextService(testWorkspace(uri.parse('file:///VSCode/workspaceLocation')))); - assert.strictEqual(service.resolve('abc ${config:editor.abc} xyz'), 'abc xyz'); - assert.strictEqual(service.resolve('abc ${config:editor.abc.def} xyz'), 'abc xyz'); - assert.strictEqual(service.resolve('abc ${config:panel} xyz'), 'abc xyz'); - assert.strictEqual(service.resolve('abc ${config:panel.abc} xyz'), 'abc xyz'); + let service = new ConfigurationResolverService(envVariables, new TestEditorService(), TestEnvironmentService, configurationService, mockCommandService); + assert.strictEqual(service.resolve(workspaceUri, 'abc ${config:editor.abc} xyz'), 'abc xyz'); + assert.strictEqual(service.resolve(workspaceUri, 'abc ${config:editor.abc.def} xyz'), 'abc xyz'); + assert.strictEqual(service.resolve(workspaceUri, 'abc ${config:panel} xyz'), 'abc xyz'); + assert.strictEqual(service.resolve(workspaceUri, 'abc ${config:panel.abc} xyz'), 'abc xyz'); }); test('is restricted to own properties', () => { @@ -252,9 +253,9 @@ suite('Configuration Resolver Service', () => { editor: {} }); - let service = new ConfigurationResolverService(envVariables, new TestEditorService(), TestEnvironmentService, configurationService, mockCommandService, new TestContextService(testWorkspace(uri.parse('file:///VSCode/workspaceLocation')))); - assert.strictEqual(service.resolve('abc ${config:editor.__proto__} xyz'), 'abc xyz'); - assert.strictEqual(service.resolve('abc ${config:editor.toString} xyz'), 'abc xyz'); + let service = new ConfigurationResolverService(envVariables, new TestEditorService(), TestEnvironmentService, configurationService, mockCommandService); + assert.strictEqual(service.resolve(workspaceUri, 'abc ${config:editor.__proto__} xyz'), 'abc xyz'); + assert.strictEqual(service.resolve(workspaceUri, 'abc ${config:editor.toString} xyz'), 'abc xyz'); }); test('configuration variables with invalid accessor', () => { @@ -265,10 +266,10 @@ suite('Configuration Resolver Service', () => { } }); - let service = new ConfigurationResolverService(envVariables, new TestEditorService(), TestEnvironmentService, configurationService, mockCommandService, new TestContextService(testWorkspace(uri.parse('file:///VSCode/workspaceLocation')))); - assert.strictEqual(service.resolve('abc ${config:} xyz'), 'abc ${config:} xyz'); - assert.strictEqual(service.resolve('abc ${config:editor..fontFamily} xyz'), 'abc xyz'); - assert.strictEqual(service.resolve('abc ${config:editor.none.none2} xyz'), 'abc xyz'); + let service = new ConfigurationResolverService(envVariables, new TestEditorService(), TestEnvironmentService, configurationService, mockCommandService); + assert.strictEqual(service.resolve(workspaceUri, 'abc ${config:} xyz'), 'abc ${config:} xyz'); + assert.strictEqual(service.resolve(workspaceUri, 'abc ${config:editor..fontFamily} xyz'), 'abc xyz'); + assert.strictEqual(service.resolve(workspaceUri, 'abc ${config:editor.none.none2} xyz'), 'abc xyz'); }); test('interactive variable simple', () => { From 6e58c166a50515c50bf68b44b277757f5f39df4c Mon Sep 17 00:00:00 2001 From: isidor Date: Tue, 25 Jul 2017 12:35:03 +0200 Subject: [PATCH 044/136] debug: start debugging select configuration --- src/vs/workbench/parts/debug/electron-browser/debugService.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/vs/workbench/parts/debug/electron-browser/debugService.ts b/src/vs/workbench/parts/debug/electron-browser/debugService.ts index 0329bee9573..e3443bb537c 100644 --- a/src/vs/workbench/parts/debug/electron-browser/debugService.ts +++ b/src/vs/workbench/parts/debug/electron-browser/debugService.ts @@ -658,6 +658,9 @@ export class DebugService implements debug.IDebugService { } else if (typeof configOrName !== 'string') { config = configOrName; } + if (launch) { + manager.selectConfiguration(launch, typeof configOrName === 'string' ? configOrName : undefined); + } if (compound) { if (!compound.configurations) { From 06550ab0c32f027740dc91eb68fe5d89ae1c35d1 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Tue, 25 Jul 2017 16:27:56 +0530 Subject: [PATCH 045/136] Version 2 --- src/vs/vscode.d.ts | 186 ++++++------------ src/vs/vscode.proposed.d.ts | 79 -------- .../mainThreadConfiguration.ts | 9 +- src/vs/workbench/api/node/extHost.api.impl.ts | 10 +- src/vs/workbench/api/node/extHost.protocol.ts | 4 +- .../api/node/extHostConfiguration.ts | 46 +++-- src/vs/workbench/api/node/extHostTypes.ts | 8 + .../api/extHostConfiguration.test.ts | 38 ++-- 8 files changed, 125 insertions(+), 255 deletions(-) diff --git a/src/vs/vscode.d.ts b/src/vs/vscode.d.ts index 8b6594e6229..c880739c315 100644 --- a/src/vs/vscode.d.ts +++ b/src/vs/vscode.d.ts @@ -2951,24 +2951,52 @@ declare module 'vscode' { } /** - * Represents the workspace configuration. + * The configuration target + */ + export enum ConfigurationTarget { + /** + * Global configuration + */ + Global = 1, + + /** + * Workspace configuration + */ + Workspace = 2, + + /** + * Workspace folder configuration + */ + WorkspaceFolder = 3 + } + + /** + * Represents the configuration. It is a merged view of * - * The workspace configuration is a merged view: Configurations of the current [workspace](#workspace.rootPath) - * (if available), files like `launch.json`, and the installation-wide configuration. Workspace specific values - * shadow installation-wide values. + * - Default configuration + * - Global configuration + * - Workspace configuration (if available) + * - Workspace folder configuration of the requested resource (if available) * - * *Note:* The merged configuration of the current [workspace](#workspace.rootPath) - * also contains settings from files like `launch.json` and `tasks.json`. Their basename will be + * **Global configuration** comes from User Settings and shadows Defaults. + * + * **Workspace configuration** comes from Workspace Settings and shadows Global configuration. + * + * **Workspace Folder configuration** comes from `.vscode` folder under one of the [workspace folders](#workspace.workspaceFolders). + * + * *Note:* Workspace and Workspace Folder configurations contains `launch` and `tasks` settings. Their basename will be * part of the section identifier. The following snippets shows how to retrieve all configurations * from `launch.json`: * * ```ts * // launch.json configuration - * const config = workspace.getConfiguration('launch'); + * const config = workspace.getConfiguration(workspace.workspaceFolders[1], 'launch'); * * // retrieve values * const values = config.get('configurations'); * ``` + * + * Refer to [Settings](https://code.visualstudio.com/docs/getstarted/settings) for more information. */ export interface WorkspaceConfiguration { @@ -2997,107 +3025,6 @@ declare module 'vscode' { */ has(section: string): boolean; - /** - * Retrieve all information about a configuration setting. A configuration value - * often consists of a *default* value, a global or installation-wide value, and - * a workspace-specific value. The *effective* value (returned by [`get`](#WorkspaceConfiguration.get)) - * is computed like this: `defaultValue` overwritten by `globalValue`, - * `globalValue` overwritten by `workspaceValue`. Refer to [Settings Inheritence](https://code.visualstudio.com/docs/getstarted/settings) - * for more information. - * - * *Note:* The configuration name must denote a leaf in the configuration tree - * (`editor.fontSize` vs `editor`) otherwise no result is returned. - * - * @param section Configuration name, supports _dotted_ names. - * @return Information about a configuration setting or `undefined`. - */ - inspect(section: string): { key: string; defaultValue?: T; globalValue?: T; workspaceValue?: T } | undefined; - - /** - * Update the global value of the configuration - * - * *Note 1:* Setting a global value in the presence of a more specific workspace value - * has no observable effect in that workspace, but in others. Refer to [Settings Inheritence](https://code.visualstudio.com/docs/getstarted/settings) - * for more information. - * - * *Note 2:* To remove a configuration value use `undefined`, like so: `config.updateFolderValue('somekey', undefined)` - * - * @param section Configuration name, supports _dotted_ names. - * @param value The new value. - */ - updateGlobalValue(section: string, value: any): Thenable; - - /** - * Update the workspace value of the configuration - * - * *Note 1:* Setting a workspace value in the presence of a more specific folder value - * has no observable effect for the resources under respective [folder](#workspace.workspaceFolders), - * but in others. Refer to [Settings Inheritence](https://code.visualstudio.com/docs/getstarted/settings) - * for more information. - * - * *Note 2:* To remove a configuration value use `undefined`, like so: `config.updateFolderValue('somekey', undefined)` - * - * @param section Configuration name, supports _dotted_ names. - * @param value The new value. - */ - updateWorkspaceValue(section: string, value: any): Thenable; - - /** - * Update a configuration value. A value can be changed for the current - * [workspace](#workspace.rootPath) only, or globally for all instances of the - * editor. The updated configuration values are persisted. - * - * *Note 1:* Setting an installation-wide value (`global: true`) in the presence of - * a more specific workspace value has no observable effect in that workspace, but - * in others. - * - * *Note 2:* To remove a configuration value use `undefined`, like so: `config.update('somekey', undefined)` - * - * @deprecated Use [`updateGlobalValue`](#WorkspaceConfiguration.updateGlobalValue) or [`updateWorkspaceValue`](#WorkspaceConfiguration.updateWorkspaceValue) instead. - * - * @param section Configuration name, supports _dotted_ names. - * @param value The new value. - * @param global When `true` changes the configuration value for all instances of the editor. - */ - update(section: string, value: any, global?: boolean): Thenable; - - /** - * Readable dictionary that backs this configuration. - */ - readonly [key: string]: any; - } - - /** - * Represents the configuration of a resource - * - * The resource configuration is a merged view of - * - * - Default configuration - * - Global configuration - * - Workspace configuration (if available) - * - Folder configuration of the resource (if available) - * - * **Global configuration** comes from User Settings and shadows Defaults. - * - * **Workspace configuration** comes from Workspace Settings and shadows Global configuration. - * - * **Folder configurations** comes from `.vscode` folder under [workspace folders](#workspace.workspaceFolders). Each [workspace folder](#workspace.workspaceFolders) - * has a configuration and the requested resource determines which folder configuration to pick. Folder configuration shodows Workspace configuration. - * - * *Note:* Workspace and Folder configurations contains settings from `launch.json` and `tasks.json` files. Their basename will be - * part of the section identifier. The following snippets shows how to retrieve all configurations - * from `launch.json`: - * - * ```ts - * // launch.json configuration - * const config = workspace.getConfiguration(workspace.workspaceFolders[1], 'launch'); - * - * // retrieve values - * const values = config.get('configurations'); - * ``` - */ - export interface ResourceConfiguration extends WorkspaceConfiguration { - /** * Retrieve all information about a configuration setting. A configuration value * often consists of a *default* value, a global or installation-wide value, @@ -3118,14 +3045,35 @@ declare module 'vscode' { inspect(section: string): { key: string; defaultValue?: T; globalValue?: T; workspaceValue?: T, folderValue?: T } | undefined; /** - * Update the folder value of the configuration + * Update a configuration value. The updated configuration values are persisted. * - * *Note:* To remove a configuration value use `undefined`, like so: `config.updateFolderValue('somekey', undefined)` + * A value can be changed in + * + * - [Global configuration](#ConfigurationTarget.Global): Changes the value for all instances of the editor. + * - [Workspace configuration](#ConfigurationTarget.Workspace): Changes the value for current workspace, if available. + * - [Workspace folder configuration](#ConfigurationTarget.WorkspaceFolder): Changes the value for the + * [Workspace folder](#workspace.workspaceFolders) to which the current [configuration](#WorkspaceConfiguration) is scoped to. + * + * *Note 1:* Setting a global value in the presence of a more specific workspace value + * has no observable effect in that workspace, but in others. Setting a workspace value + * in the presence of a more specific folder value has no observable effect for the resources + * under respective [folder](#workspace.workspaceFolders), but in others. Refer to + * [Settings Inheritence](https://code.visualstudio.com/docs/getstarted/settings) for more information. + * + * *Note 2:* To remove a configuration value use `undefined`, like so: `config.update('somekey', undefined)` * * @param section Configuration name, supports _dotted_ names. * @param value The new value. + * @param configurationTarget The [configuration target](#ConfigurationTarget) + * @param global When `true` changes the global configuration value otherwise workspace configuration value */ - updateFolderValue(section: string, value: any): Thenable; + update(section: string, value: any, configurationTarget: ConfigurationTarget): Thenable; + update(section: string, value: any, global?: boolean): Thenable; + + /** + * Readable dictionary that backs this configuration. + */ + readonly [key: string]: any; } /** @@ -4947,23 +4895,13 @@ declare module 'vscode' { * is returned. Dots in the section-identifier are interpreted as child-access, * like `{ myExt: { setting: { doIt: true }}}` and `getConfiguration('myExt.setting').get('doIt') === true`. * - * @param section A dot-separated identifier. - * @return The full configuration or a subset. - */ - export function getConfiguration(section?: string): WorkspaceConfiguration; - - /** - * Get a resource configuration object. - * - * When a section-identifier is provided only that part of the configuration - * is returned. Dots in the section-identifier are interpreted as child-access, - * like `{ myExt: { setting: { doIt: true }}}` and `getConfiguration('myExt.setting').get('doIt') === true`. + * When a resource is provided, configuration scoped to that resource is returned. * * @param section A dot-separated identifier. - * @param resource A resource for which configuration is asked for + * @param resource A resource for which the configuration is asked for * @return The full configuration or a subset. */ - export function getConfiguration(resource: Uri, section?: string): ResourceConfiguration; + export function getConfiguration(section?: string, resource?: Uri): WorkspaceConfiguration; /** * An event that is emitted when the [configuration](#WorkspaceConfiguration) changed. diff --git a/src/vs/vscode.proposed.d.ts b/src/vs/vscode.proposed.d.ts index 79c14ff35e9..8dd39d6f190 100644 --- a/src/vs/vscode.proposed.d.ts +++ b/src/vs/vscode.proposed.d.ts @@ -7,12 +7,6 @@ declare module 'vscode' { - export interface WorkspaceConfiguration2 extends WorkspaceConfiguration { - - inspect(section: string): { key: string; defaultValue?: T; globalValue?: T; workspaceValue?: T, folderValue?: T } | undefined; - - } - // todo@joh discover files etc export interface FileSystemProvider { // todo@joh -> added, deleted, renamed, changed @@ -25,79 +19,6 @@ declare module 'vscode' { export namespace workspace { export function registerFileSystemProvider(authority: string, provider: FileSystemProvider): Disposable; - - /** - * Get a configuration object. - * - * When a section-identifier is provided only that part of the configuration - * is returned. Dots in the section-identifier are interpreted as child-access, - * like `{ myExt: { setting: { doIt: true }}}` and `getConfiguration('myExt.setting').get('doIt') === true`. - * - * When a resource is provided, only configuration scoped to that resource - * is returned. - * - * If editor is opened with `no folders` then returns the global configuration. - * - * If editor is opened with `folders` then returns the configuration from the folder in which the resource belongs to. - * - * If resource does not belongs to any opened folders, then returns the workspace configuration. - * - * @param section A dot-separated identifier. - * @param resource A resource for which configuration is asked - * @return The full workspace configuration or a subset. - */ - export function getConfiguration2(section?: string, resource?: Uri): WorkspaceConfiguration2; - } - - /** - * Represents the workspace configuration. - * - * The workspace configuration is a merged view of - * - * - Default configuration - * - Global configuration - * - Workspace configuration (if available) - * - Folder configuration of the [resource](#workspace.getConfiguration2) (if requested and available) - * - * **Global configuration** comes from User Settings and shadows Defaults. - * - * **Workspace configuration** comes from the `.vscode` folder under first [workspace folders](#workspace.workspaceFolders) - * and shadows Globals configuration. - * - * **Folder configurations** comes from `.vscode` folder under [workspace folders](#workspace.workspaceFolders). Each [workspace folder](#workspace.workspaceFolders) - * has a configuration and the requested resource determines which folder configuration to pick. Folder configuration shodows Workspace configuration. - * - * *Note:* Workspace and Folder configurations contains settings from `launch.json` and `tasks.json` files. Their basename will be - * part of the section identifier. The following snippets shows how to retrieve all configurations - * from `launch.json`: - * - * ```ts - * // launch.json configuration - * const config = workspace.getConfiguration('launch', workspace.workspaceFolders[1]); - * - * // retrieve values - * const values = config.get('configurations'); - * ``` - */ - export interface WorkspaceConfiguration2 extends WorkspaceConfiguration { - - /** - * Retrieve all information about a configuration setting. A configuration value - * often consists of a *default* value, a global or installation-wide value, - * a workspace-specific value and a folder-specific value. - * - * The *effective* value (returned by [`get`](#WorkspaceConfiguration.get)) - * is computed like this: `defaultValue` overwritten by `globalValue`, - * `globalValue` overwritten by `workspaceValue`. `workspaceValue` overwritten by `folderValue`. - * - * *Note:* The configuration name must denote a leaf in the configuration tree - * (`editor.fontSize` vs `editor`) otherwise no result is returned. - * - * @param section Configuration name, supports _dotted_ names. - * @return Information about a configuration setting or `undefined`. - */ - inspect(section: string): { key: string; defaultValue?: T; globalValue?: T; workspaceValue?: T, folderValue?: T } | undefined; - } export namespace window { diff --git a/src/vs/workbench/api/electron-browser/mainThreadConfiguration.ts b/src/vs/workbench/api/electron-browser/mainThreadConfiguration.ts index b08d8877d07..6d3b48ea2bf 100644 --- a/src/vs/workbench/api/electron-browser/mainThreadConfiguration.ts +++ b/src/vs/workbench/api/electron-browser/mainThreadConfiguration.ts @@ -4,6 +4,7 @@ *--------------------------------------------------------------------------------------------*/ 'use strict'; +import URI from 'vs/base/common/uri'; import { TPromise } from 'vs/base/common/winjs.base'; import { IDisposable, dispose } from 'vs/base/common/lifecycle'; import { IThreadService } from 'vs/workbench/services/thread/common/threadService'; @@ -34,11 +35,11 @@ export class MainThreadConfiguration extends MainThreadConfigurationShape { this._toDispose = dispose(this._toDispose); } - $updateConfigurationOption(target: ConfigurationTarget, key: string, value: any): TPromise { - return this._configurationEditingService.writeConfiguration(target, { key, value }, { donotNotifyError: true }); + $updateConfigurationOption(target: ConfigurationTarget, key: string, value: any, resource: URI): TPromise { + return this._configurationEditingService.writeConfiguration(target, { key, value }, { donotNotifyError: true, scopes: { resource } }); } - $removeConfigurationOption(target: ConfigurationTarget, key: string): TPromise { - return this._configurationEditingService.writeConfiguration(target, { key, value: undefined }, { donotNotifyError: true }); + $removeConfigurationOption(target: ConfigurationTarget, key: string, resource: URI): TPromise { + return this._configurationEditingService.writeConfiguration(target, { key, value: undefined }, { donotNotifyError: true, scopes: { resource } }); } } diff --git a/src/vs/workbench/api/node/extHost.api.impl.ts b/src/vs/workbench/api/node/extHost.api.impl.ts index 8a2987fdf69..d67938243a1 100644 --- a/src/vs/workbench/api/node/extHost.api.impl.ts +++ b/src/vs/workbench/api/node/extHost.api.impl.ts @@ -445,12 +445,9 @@ export function createApiFactory( onDidChangeConfiguration: (listener: (_: any) => any, thisArgs?: any, disposables?: extHostTypes.Disposable[]) => { return extHostConfiguration.onDidChangeConfiguration(listener, thisArgs, disposables); }, - getConfiguration: (section?: string): vscode.WorkspaceConfiguration => { - return extHostConfiguration.getConfiguration(section); + getConfiguration: (section?: string, resource?: vscode.Uri): vscode.WorkspaceConfiguration => { + return extHostConfiguration.getConfiguration(section, resource); }, - getConfiguration2: proposedApiFunction(extension, (section?: string, resource?: vscode.Uri): vscode.WorkspaceConfiguration => { - return extHostConfiguration.getConfiguration2(section, resource); - }), registerTaskProvider: (type: string, provider: vscode.TaskProvider) => { return extHostTask.registerTaskProvider(extension, provider); }, @@ -578,7 +575,8 @@ export function createApiFactory( TaskGroup: extHostTypes.TaskGroup, ProcessExecution: extHostTypes.ProcessExecution, ShellExecution: extHostTypes.ShellExecution, - Task: extHostTypes.Task + Task: extHostTypes.Task, + ConfigurationTarget: extHostTypes.ConfigurationTarget }; }; } diff --git a/src/vs/workbench/api/node/extHost.protocol.ts b/src/vs/workbench/api/node/extHost.protocol.ts index 19e2b61963c..f9b11ee296f 100644 --- a/src/vs/workbench/api/node/extHost.protocol.ts +++ b/src/vs/workbench/api/node/extHost.protocol.ts @@ -123,8 +123,8 @@ export abstract class MainThreadCommandsShape { } export abstract class MainThreadConfigurationShape { - $updateConfigurationOption(target: ConfigurationTarget, key: string, value: any): TPromise { throw ni(); } - $removeConfigurationOption(target: ConfigurationTarget, key: string): TPromise { throw ni(); } + $updateConfigurationOption(target: ConfigurationTarget, key: string, value: any, resource: URI): TPromise { throw ni(); } + $removeConfigurationOption(target: ConfigurationTarget, key: string, resource: URI): TPromise { throw ni(); } } export abstract class MainThreadDiagnosticsShape { diff --git a/src/vs/workbench/api/node/extHostConfiguration.ts b/src/vs/workbench/api/node/extHostConfiguration.ts index b900063a378..77e42189cc6 100644 --- a/src/vs/workbench/api/node/extHostConfiguration.ts +++ b/src/vs/workbench/api/node/extHostConfiguration.ts @@ -7,9 +7,10 @@ import { mixin } from 'vs/base/common/objects'; import URI from 'vs/base/common/uri'; import Event, { Emitter } from 'vs/base/common/event'; -import { WorkspaceConfiguration, WorkspaceConfiguration2 } from 'vscode'; +import { WorkspaceConfiguration } from 'vscode'; import { ExtHostWorkspace } from 'vs/workbench/api/node/extHostWorkspace'; import { ExtHostConfigurationShape, MainThreadConfigurationShape } from './extHost.protocol'; +import { ConfigurationTarget as ExtHostConfigurationTarget } from './extHostTypes'; import { IConfigurationData, Configuration } from 'vs/platform/configuration/common/configuration'; import { ConfigurationTarget } from 'vs/workbench/services/configuration/common/configurationEditing'; @@ -55,20 +56,26 @@ export class ExtHostConfiguration extends ExtHostConfigurationShape { this._onDidChangeConfiguration.fire(undefined); } - getConfiguration(section?: string): WorkspaceConfiguration { - return this._getConfiguration(section, null, true); - } - - getConfiguration2(section?: string, resource?: URI): WorkspaceConfiguration2 { - return this._getConfiguration(section, resource, false); - } - - private _getConfiguration(section: string, resource: URI, legacy: boolean): WorkspaceConfiguration { - + getConfiguration(section?: string, resource?: URI): WorkspaceConfiguration { const config = section ? lookUp(this._configuration.getValue(null, { resource }), section) : this._configuration.getValue(null, { resource }); + function parseConfigurationTarget(arg: boolean | ExtHostConfigurationTarget): ConfigurationTarget { + if (arg === void 0 || arg === null) { + return ConfigurationTarget.WORKSPACE; + } + if (typeof arg === 'boolean') { + return arg ? ConfigurationTarget.USER : ConfigurationTarget.WORKSPACE; + } + + switch (arg) { + case ExtHostConfigurationTarget.Global: return ConfigurationTarget.USER; + case ExtHostConfigurationTarget.Workspace: return ConfigurationTarget.WORKSPACE; + case ExtHostConfigurationTarget.WorkspaceFolder: return ConfigurationTarget.FOLDER; + } + } + const result: WorkspaceConfiguration = { has(key: string): boolean { return typeof lookUp(config, key) !== 'undefined'; @@ -80,29 +87,26 @@ export class ExtHostConfiguration extends ExtHostConfigurationShape { } return result; }, - update: (key: string, value: any, global: boolean = false) => { + update: (key: string, value: any, arg: boolean | ExtHostConfigurationTarget) => { key = section ? `${section}.${key}` : key; - const target = global ? ConfigurationTarget.USER : ConfigurationTarget.WORKSPACE; + const target = parseConfigurationTarget(arg); if (value !== void 0) { - return this._proxy.$updateConfigurationOption(target, key, value); + return this._proxy.$updateConfigurationOption(target, key, value, resource); } else { - return this._proxy.$removeConfigurationOption(target, key); + return this._proxy.$removeConfigurationOption(target, key, resource); } }, inspect: (key: string): ConfigurationInspect => { key = section ? `${section}.${key}` : key; - const config = legacy ? this._configuration.lookupLegacy(key) : this._configuration.lookup(key, { resource }); + const config = this._configuration.lookup(key, { resource }); if (config) { - const inspect: ConfigurationInspect = { + return { key, defaultValue: config.default, globalValue: config.user, workspaceValue: config.workspace, + folderValue: config.folder }; - if (!legacy) { - inspect.folderValue = config.folder; - } - return inspect; } return undefined; } diff --git a/src/vs/workbench/api/node/extHostTypes.ts b/src/vs/workbench/api/node/extHostTypes.ts index 790d0df89ba..ab6e6d59b32 100644 --- a/src/vs/workbench/api/node/extHostTypes.ts +++ b/src/vs/workbench/api/node/extHostTypes.ts @@ -1316,3 +1316,11 @@ export class ThemeColor { this.id = id; } } + +export enum ConfigurationTarget { + Global = 1, + + Workspace = 2, + + WorkspaceFolder = 3 +} \ No newline at end of file diff --git a/src/vs/workbench/test/electron-browser/api/extHostConfiguration.test.ts b/src/vs/workbench/test/electron-browser/api/extHostConfiguration.test.ts index 7a70b22c844..accbd651fc6 100644 --- a/src/vs/workbench/test/electron-browser/api/extHostConfiguration.test.ts +++ b/src/vs/workbench/test/electron-browser/api/extHostConfiguration.test.ts @@ -108,13 +108,13 @@ suite('ExtHostConfiguration', function () { assert.equal(actual.defaultValue, 'off'); assert.equal(actual.globalValue, 'on'); assert.equal(actual.workspaceValue, undefined); - assert.ok(Object.keys(actual).indexOf('folderValue') === -1); + assert.equal(actual.folderValue, undefined); actual = testObject.getConfiguration('editor').inspect('wordWrap'); assert.equal(actual.defaultValue, 'off'); assert.equal(actual.globalValue, 'on'); assert.equal(actual.workspaceValue, undefined); - assert.ok(Object.keys(actual).indexOf('folderValue') === -1); + assert.equal(actual.folderValue, undefined); }); test('inspect in single root context', function () { @@ -153,21 +153,21 @@ suite('ExtHostConfiguration', function () { assert.equal(actual1.defaultValue, 'off'); assert.equal(actual1.globalValue, 'on'); assert.equal(actual1.workspaceValue, 'bounded'); - assert.ok(Object.keys(actual1).indexOf('folderValue') === -1); + assert.equal(actual1.folderValue, undefined); actual1 = testObject.getConfiguration('editor').inspect('wordWrap'); assert.equal(actual1.defaultValue, 'off'); assert.equal(actual1.globalValue, 'on'); assert.equal(actual1.workspaceValue, 'bounded'); - assert.ok(Object.keys(actual1).indexOf('folderValue') === -1); + assert.equal(actual1.folderValue, undefined); - let actual2 = testObject.getConfiguration2(null, workspaceUri).inspect('editor.wordWrap'); + let actual2 = testObject.getConfiguration(null, workspaceUri).inspect('editor.wordWrap'); assert.equal(actual2.defaultValue, 'off'); assert.equal(actual2.globalValue, 'on'); assert.equal(actual2.workspaceValue, 'bounded'); assert.equal(actual2.folderValue, 'bounded'); - actual2 = testObject.getConfiguration2('editor', workspaceUri).inspect('wordWrap'); + actual2 = testObject.getConfiguration('editor', workspaceUri).inspect('wordWrap'); assert.equal(actual2.defaultValue, 'off'); assert.equal(actual2.globalValue, 'on'); assert.equal(actual2.workspaceValue, 'bounded'); @@ -225,59 +225,59 @@ suite('ExtHostConfiguration', function () { let actual1 = testObject.getConfiguration().inspect('editor.wordWrap'); assert.equal(actual1.defaultValue, 'off'); assert.equal(actual1.globalValue, 'on'); - assert.equal(actual1.workspaceValue, 'off'); - assert.ok(Object.keys(actual1).indexOf('folderValue') === -1); + assert.equal(actual1.workspaceValue, 'bounded'); + assert.equal(actual1.folderValue, undefined); actual1 = testObject.getConfiguration('editor').inspect('wordWrap'); assert.equal(actual1.defaultValue, 'off'); assert.equal(actual1.globalValue, 'on'); - assert.equal(actual1.workspaceValue, 'off'); - assert.ok(Object.keys(actual1).indexOf('folderValue') === -1); + assert.equal(actual1.workspaceValue, 'bounded'); + assert.equal(actual1.folderValue, undefined); actual1 = testObject.getConfiguration('editor').inspect('lineNumbers'); assert.equal(actual1.defaultValue, 'on'); assert.equal(actual1.globalValue, undefined); - assert.equal(actual1.workspaceValue, 'relative'); - assert.ok(Object.keys(actual1).indexOf('folderValue') === -1); + assert.equal(actual1.workspaceValue, undefined); + assert.equal(actual1.folderValue, undefined); - let actual2 = testObject.getConfiguration2(null, firstRoot).inspect('editor.wordWrap'); + let actual2 = testObject.getConfiguration(null, firstRoot).inspect('editor.wordWrap'); assert.equal(actual2.defaultValue, 'off'); assert.equal(actual2.globalValue, 'on'); assert.equal(actual2.workspaceValue, 'bounded'); assert.equal(actual2.folderValue, 'off'); - actual2 = testObject.getConfiguration2('editor', firstRoot).inspect('wordWrap'); + actual2 = testObject.getConfiguration('editor', firstRoot).inspect('wordWrap'); assert.equal(actual2.defaultValue, 'off'); assert.equal(actual2.globalValue, 'on'); assert.equal(actual2.workspaceValue, 'bounded'); assert.equal(actual2.folderValue, 'off'); - actual2 = testObject.getConfiguration2('editor', firstRoot).inspect('lineNumbers'); + actual2 = testObject.getConfiguration('editor', firstRoot).inspect('lineNumbers'); assert.equal(actual2.defaultValue, 'on'); assert.equal(actual2.globalValue, undefined); assert.equal(actual2.workspaceValue, undefined); assert.equal(actual2.folderValue, 'relative'); - actual2 = testObject.getConfiguration2(null, secondRoot).inspect('editor.wordWrap'); + actual2 = testObject.getConfiguration(null, secondRoot).inspect('editor.wordWrap'); assert.equal(actual2.defaultValue, 'off'); assert.equal(actual2.globalValue, 'on'); assert.equal(actual2.workspaceValue, 'bounded'); assert.equal(actual2.folderValue, 'on'); - actual2 = testObject.getConfiguration2('editor', secondRoot).inspect('wordWrap'); + actual2 = testObject.getConfiguration('editor', secondRoot).inspect('wordWrap'); assert.equal(actual2.defaultValue, 'off'); assert.equal(actual2.globalValue, 'on'); assert.equal(actual2.workspaceValue, 'bounded'); assert.equal(actual2.folderValue, 'on'); - actual2 = testObject.getConfiguration2(null, thirdRoot).inspect('editor.wordWrap'); + actual2 = testObject.getConfiguration(null, thirdRoot).inspect('editor.wordWrap'); assert.equal(actual2.defaultValue, 'off'); assert.equal(actual2.globalValue, 'on'); assert.equal(actual2.workspaceValue, 'bounded'); assert.ok(Object.keys(actual2).indexOf('folderValue') !== -1); assert.equal(actual2.folderValue, undefined); - actual2 = testObject.getConfiguration2('editor', thirdRoot).inspect('wordWrap'); + actual2 = testObject.getConfiguration('editor', thirdRoot).inspect('wordWrap'); assert.equal(actual2.defaultValue, 'off'); assert.equal(actual2.globalValue, 'on'); assert.equal(actual2.workspaceValue, 'bounded'); From 8d23acdb5502e1e2bedc31b93fe67acf3a4878e0 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Tue, 25 Jul 2017 17:08:59 +0530 Subject: [PATCH 046/136] Make @enabled extensions part of installed extensions --- .../parts/extensions/electron-browser/extensionsViews.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/parts/extensions/electron-browser/extensionsViews.ts b/src/vs/workbench/parts/extensions/electron-browser/extensionsViews.ts index 858e07486cd..4e1d029f564 100644 --- a/src/vs/workbench/parts/extensions/electron-browser/extensionsViews.ts +++ b/src/vs/workbench/parts/extensions/electron-browser/extensionsViews.ts @@ -420,6 +420,10 @@ export class ExtensionsListView extends CollapsibleView { return /@disabled/i.test(query); } + static isEnabledExtensionsQuery(query: string): boolean { + return /@enabled/i.test(query); + } + static isRecommendedExtensionsQuery(query: string): boolean { return /@recommended/i.test(query); } @@ -438,7 +442,8 @@ export class InstalledExtensionsView extends ExtensionsListView { public static isInsalledExtensionsQuery(query: string): boolean { return ExtensionsListView.isInstalledExtensionsQuery(query) || ExtensionsListView.isOutdatedExtensionsQuery(query) - || ExtensionsListView.isDisabledExtensionsQuery(query); + || ExtensionsListView.isDisabledExtensionsQuery(query) + || ExtensionsListView.isEnabledExtensionsQuery(query); } async show(query: string): TPromise> { From 122cc332f290ba319f18a1ec34b2c039a5b0411a Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Tue, 25 Jul 2017 17:14:42 +0530 Subject: [PATCH 047/136] follow up changes to @enabled query PR --- .../extensions/electron-browser/extensionsViewlet.ts | 2 +- .../parts/extensions/electron-browser/extensionsViews.ts | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/vs/workbench/parts/extensions/electron-browser/extensionsViewlet.ts b/src/vs/workbench/parts/extensions/electron-browser/extensionsViewlet.ts index 5eaa386da77..218262d0428 100644 --- a/src/vs/workbench/parts/extensions/electron-browser/extensionsViewlet.ts +++ b/src/vs/workbench/parts/extensions/electron-browser/extensionsViewlet.ts @@ -264,9 +264,9 @@ export class ExtensionsViewlet extends ComposedViewsViewlet implements IExtensio getSecondaryActions(): IAction[] { if (!this.secondaryActions) { this.secondaryActions = [ - this.instantiationService.createInstance(ShowEnabledExtensionsAction, ShowEnabledExtensionsAction.ID, ShowEnabledExtensionsAction.LABEL), this.instantiationService.createInstance(ShowInstalledExtensionsAction, ShowInstalledExtensionsAction.ID, ShowInstalledExtensionsAction.LABEL), this.instantiationService.createInstance(ShowOutdatedExtensionsAction, ShowOutdatedExtensionsAction.ID, ShowOutdatedExtensionsAction.LABEL), + this.instantiationService.createInstance(ShowEnabledExtensionsAction, ShowEnabledExtensionsAction.ID, ShowEnabledExtensionsAction.LABEL), this.instantiationService.createInstance(ShowDisabledExtensionsAction, ShowDisabledExtensionsAction.ID, ShowDisabledExtensionsAction.LABEL), this.instantiationService.createInstance(ShowRecommendedExtensionsAction, ShowRecommendedExtensionsAction.ID, ShowRecommendedExtensionsAction.LABEL), this.instantiationService.createInstance(ShowWorkspaceRecommendedExtensionsAction, ShowWorkspaceRecommendedExtensionsAction.ID, ShowWorkspaceRecommendedExtensionsAction.LABEL), diff --git a/src/vs/workbench/parts/extensions/electron-browser/extensionsViews.ts b/src/vs/workbench/parts/extensions/electron-browser/extensionsViews.ts index 4e1d029f564..c102812b031 100644 --- a/src/vs/workbench/parts/extensions/electron-browser/extensionsViews.ts +++ b/src/vs/workbench/parts/extensions/electron-browser/extensionsViews.ts @@ -202,8 +202,8 @@ export class ExtensionsListView extends CollapsibleView { return new PagedModel(result); } - if (/(@disabled|@enabled:false)/i.test(value)) { - value = value.replace(/(@disabled|@enabled:false)/g, '').trim().toLowerCase(); + if (/@disabled/i.test(value)) { + value = value.replace(/@disabled/g, '').trim().toLowerCase(); const local = await this.extensionsWorkbenchService.queryLocal(); const runningExtensions = await this.extensionService.getExtensions(); @@ -215,8 +215,8 @@ export class ExtensionsListView extends CollapsibleView { return new PagedModel(result); } - if (/@enabled(:true)?/i.test(value)) { - value = value ? value.replace(/@enabled(:true)?/g, '').trim().toLowerCase() : ''; + if (/@enabled/i.test(value)) { + value = value ? value.replace(/@enabled/g, '').trim().toLowerCase() : ''; const local = await this.extensionsWorkbenchService.queryLocal(); From 24d0f5f8a0e548a47284769660a83303a61043a5 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Tue, 25 Jul 2017 11:33:19 +0200 Subject: [PATCH 048/136] prep for #31124 --- .../contrib/snippet/browser/snippetParser.ts | 23 ++++++++-- .../snippet/browser/snippetVariables.ts | 8 +++- .../test/browser/snippetVariables.test.ts | 44 +++++++++---------- 3 files changed, 48 insertions(+), 27 deletions(-) diff --git a/src/vs/editor/contrib/snippet/browser/snippetParser.ts b/src/vs/editor/contrib/snippet/browser/snippetParser.ts index 2f6b6252f9c..f4261a7f829 100644 --- a/src/vs/editor/contrib/snippet/browser/snippetParser.ts +++ b/src/vs/editor/contrib/snippet/browser/snippetParser.ts @@ -167,6 +167,19 @@ export abstract class Marker { return this._children; } + get snippet(): TextmateSnippet { + let candidate: Marker = this; + while (true) { + if (!candidate) { + return undefined; + } + if (candidate instanceof TextmateSnippet) { + return candidate; + } + candidate = candidate.parent; + } + } + toString() { return ''; } @@ -269,8 +282,8 @@ export class Variable extends Marker { super(); } - resolve(resolver: { resolve(name: string): string }): boolean { - const value = resolver.resolve(this.name); + resolve(resolver: VariableResolver): boolean { + const value = resolver.resolve(this); if (value !== undefined) { this._children = [new Text(value)]; return true; @@ -289,6 +302,10 @@ export class Variable extends Marker { } } +export interface VariableResolver { + resolve(variable: Variable): string | undefined; +} + function walk(marker: Marker[], visitor: (marker: Marker) => boolean): void { const stack = [...marker]; while (stack.length > 0) { @@ -362,7 +379,7 @@ export class TextmateSnippet extends Marker { return Marker.toString(this.children); } - resolveVariables(resolver: { resolve(name: string): string }): this { + resolveVariables(resolver: VariableResolver): this { this.walk(candidate => { if (candidate instanceof Variable) { if (candidate.resolve(resolver)) { diff --git a/src/vs/editor/contrib/snippet/browser/snippetVariables.ts b/src/vs/editor/contrib/snippet/browser/snippetVariables.ts index 2953ee8bfc3..15d6173631b 100644 --- a/src/vs/editor/contrib/snippet/browser/snippetVariables.ts +++ b/src/vs/editor/contrib/snippet/browser/snippetVariables.ts @@ -8,8 +8,9 @@ import { basename, dirname } from 'vs/base/common/paths'; import { IModel } from 'vs/editor/common/editorCommon'; import { Selection } from 'vs/editor/common/core/selection'; +import { VariableResolver, Variable } from 'vs/editor/contrib/snippet/browser/snippetParser'; -export class EditorSnippetVariableResolver { +export class EditorSnippetVariableResolver implements VariableResolver { static readonly VariableNames = Object.freeze({ 'SELECTION': true, @@ -30,7 +31,10 @@ export class EditorSnippetVariableResolver { // } - resolve(name: string): string { + resolve(variable: Variable): string { + + const { name } = variable; + if (name === 'SELECTION' || name === 'TM_SELECTED_TEXT') { return this._model.getValueInRange(this._selection) || undefined; diff --git a/src/vs/editor/contrib/snippet/test/browser/snippetVariables.test.ts b/src/vs/editor/contrib/snippet/test/browser/snippetVariables.test.ts index fc9c6715b20..a21516d37d8 100644 --- a/src/vs/editor/contrib/snippet/test/browser/snippetVariables.test.ts +++ b/src/vs/editor/contrib/snippet/test/browser/snippetVariables.test.ts @@ -9,7 +9,7 @@ import { isWindows } from 'vs/base/common/platform'; import URI from 'vs/base/common/uri'; import { Selection } from 'vs/editor/common/core/selection'; import { EditorSnippetVariableResolver } from 'vs/editor/contrib/snippet/browser/snippetVariables'; -import { SnippetParser } from 'vs/editor/contrib/snippet/browser/snippetParser'; +import { SnippetParser, Variable } from 'vs/editor/contrib/snippet/browser/snippetParser'; import { Model } from 'vs/editor/common/model/model'; suite('Snippet Variables Resolver', function () { @@ -32,58 +32,58 @@ suite('Snippet Variables Resolver', function () { }); test('editor variables, basics', function () { - assert.equal(resolver.resolve('TM_FILENAME'), 'text.txt'); - assert.equal(resolver.resolve('something'), undefined); + assert.equal(resolver.resolve(new Variable('TM_FILENAME')), 'text.txt'); + assert.equal(resolver.resolve(new Variable('something')), undefined); }); test('editor variables, file/dir', function () { - assert.equal(resolver.resolve('TM_FILENAME'), 'text.txt'); + assert.equal(resolver.resolve(new Variable('TM_FILENAME')), 'text.txt'); if (!isWindows) { - assert.equal(resolver.resolve('TM_DIRECTORY'), '/foo/files'); - assert.equal(resolver.resolve('TM_FILEPATH'), '/foo/files/text.txt'); + assert.equal(resolver.resolve(new Variable('TM_DIRECTORY')), '/foo/files'); + assert.equal(resolver.resolve(new Variable('TM_FILEPATH')), '/foo/files/text.txt'); } resolver = new EditorSnippetVariableResolver( Model.createFromString('', undefined, undefined, URI.parse('http://www.pb.o/abc/def/ghi')), new Selection(1, 1, 1, 1) ); - assert.equal(resolver.resolve('TM_FILENAME'), 'ghi'); + assert.equal(resolver.resolve(new Variable('TM_FILENAME')), 'ghi'); if (!isWindows) { - assert.equal(resolver.resolve('TM_DIRECTORY'), '/abc/def'); - assert.equal(resolver.resolve('TM_FILEPATH'), '/abc/def/ghi'); + assert.equal(resolver.resolve(new Variable('TM_DIRECTORY')), '/abc/def'); + assert.equal(resolver.resolve(new Variable('TM_FILEPATH')), '/abc/def/ghi'); } resolver = new EditorSnippetVariableResolver( Model.createFromString('', undefined, undefined, URI.parse('mem:fff.ts')), new Selection(1, 1, 1, 1) ); - assert.equal(resolver.resolve('TM_DIRECTORY'), ''); - assert.equal(resolver.resolve('TM_FILEPATH'), 'fff.ts'); + assert.equal(resolver.resolve(new Variable('TM_DIRECTORY')), ''); + assert.equal(resolver.resolve(new Variable('TM_FILEPATH')), 'fff.ts'); }); test('editor variables, selection', function () { resolver = new EditorSnippetVariableResolver(model, new Selection(1, 2, 2, 3)); - assert.equal(resolver.resolve('TM_SELECTED_TEXT'), 'his is line one\nth'); - assert.equal(resolver.resolve('TM_CURRENT_LINE'), 'this is line two'); - assert.equal(resolver.resolve('TM_LINE_INDEX'), '1'); - assert.equal(resolver.resolve('TM_LINE_NUMBER'), '2'); + assert.equal(resolver.resolve(new Variable('TM_SELECTED_TEXT')), 'his is line one\nth'); + assert.equal(resolver.resolve(new Variable('TM_CURRENT_LINE')), 'this is line two'); + assert.equal(resolver.resolve(new Variable('TM_LINE_INDEX')), '1'); + assert.equal(resolver.resolve(new Variable('TM_LINE_NUMBER')), '2'); resolver = new EditorSnippetVariableResolver(model, new Selection(2, 3, 1, 2)); - assert.equal(resolver.resolve('TM_SELECTED_TEXT'), 'his is line one\nth'); - assert.equal(resolver.resolve('TM_CURRENT_LINE'), 'this is line one'); - assert.equal(resolver.resolve('TM_LINE_INDEX'), '0'); - assert.equal(resolver.resolve('TM_LINE_NUMBER'), '1'); + assert.equal(resolver.resolve(new Variable('TM_SELECTED_TEXT')), 'his is line one\nth'); + assert.equal(resolver.resolve(new Variable('TM_CURRENT_LINE')), 'this is line one'); + assert.equal(resolver.resolve(new Variable('TM_LINE_INDEX')), '0'); + assert.equal(resolver.resolve(new Variable('TM_LINE_NUMBER')), '1'); resolver = new EditorSnippetVariableResolver(model, new Selection(1, 2, 1, 2)); - assert.equal(resolver.resolve('TM_SELECTED_TEXT'), undefined); + assert.equal(resolver.resolve(new Variable('TM_SELECTED_TEXT')), undefined); - assert.equal(resolver.resolve('TM_CURRENT_WORD'), 'this'); + assert.equal(resolver.resolve(new Variable('TM_CURRENT_WORD')), 'this'); resolver = new EditorSnippetVariableResolver(model, new Selection(3, 1, 3, 1)); - assert.equal(resolver.resolve('TM_CURRENT_WORD'), undefined); + assert.equal(resolver.resolve(new Variable('TM_CURRENT_WORD')), undefined); }); From bca2236bd5d91d197917defbdb2e3ce6f63988e4 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Tue, 25 Jul 2017 14:55:42 +0200 Subject: [PATCH 049/136] normalize selected text variable when resolving it, #31124 --- .../snippet/browser/snippetVariables.ts | 32 ++++++++++- .../test/browser/snippetSession.test.ts | 48 +++++++++++++++++ .../test/browser/snippetVariables.test.ts | 53 +++++++++++-------- 3 files changed, 110 insertions(+), 23 deletions(-) diff --git a/src/vs/editor/contrib/snippet/browser/snippetVariables.ts b/src/vs/editor/contrib/snippet/browser/snippetVariables.ts index 15d6173631b..25780d7dbcd 100644 --- a/src/vs/editor/contrib/snippet/browser/snippetVariables.ts +++ b/src/vs/editor/contrib/snippet/browser/snippetVariables.ts @@ -8,7 +8,8 @@ import { basename, dirname } from 'vs/base/common/paths'; import { IModel } from 'vs/editor/common/editorCommon'; import { Selection } from 'vs/editor/common/core/selection'; -import { VariableResolver, Variable } from 'vs/editor/contrib/snippet/browser/snippetParser'; +import { VariableResolver, Variable, Text } from 'vs/editor/contrib/snippet/browser/snippetParser'; +import { getLeadingWhitespace, commonPrefixLength } from 'vs/base/common/strings'; export class EditorSnippetVariableResolver implements VariableResolver { @@ -36,7 +37,34 @@ export class EditorSnippetVariableResolver implements VariableResolver { const { name } = variable; if (name === 'SELECTION' || name === 'TM_SELECTED_TEXT') { - return this._model.getValueInRange(this._selection) || undefined; + let value = this._model.getValueInRange(this._selection) || undefined; + if (value && this._selection.startLineNumber !== this._selection.endLineNumber) { + // Selection is a multiline string which we indentation we now + // need to adjust. We compare the indentation of this variable + // with the indentation at the editor position and add potential + // extra indentation to the value + + const line = this._model.getLineContent(this._selection.startLineNumber); + const lineLeadingWhitespace = getLeadingWhitespace(line, 0, this._selection.startColumn - 1); + + let varLeadingWhitespace = lineLeadingWhitespace; + variable.snippet.walk(marker => { + if (marker === variable) { + return false; + } + if (marker instanceof Text) { + varLeadingWhitespace = getLeadingWhitespace(marker.value.split(/\r\n|\r|\n/).pop()); + } + return true; + }); + const whitespaceCommonLength = commonPrefixLength(varLeadingWhitespace, lineLeadingWhitespace); + + value = value.replace( + /(\r\n|\r|\n)(.*)/g, + (m, newline, rest) => `${newline}${varLeadingWhitespace.substr(whitespaceCommonLength)}${rest}` + ); + } + return value; } else if (name === 'TM_CURRENT_LINE') { return this._model.getLineContent(this._selection.positionLineNumber); diff --git a/src/vs/editor/contrib/snippet/test/browser/snippetSession.test.ts b/src/vs/editor/contrib/snippet/test/browser/snippetSession.test.ts index 62f148f8ff8..26e401d6f6f 100644 --- a/src/vs/editor/contrib/snippet/test/browser/snippetSession.test.ts +++ b/src/vs/editor/contrib/snippet/test/browser/snippetSession.test.ts @@ -462,5 +462,53 @@ suite('SnippetSession', function () { assert.equal(editor.getModel().getValue(), 'test 1\ntest 2\ntest 3\ntest 4\n'); }); + + test('Snippet variable text isn\'t whitespace normalised, #31124', function () { + editor.getModel().setValue([ + 'start', + '\t\t-one', + '\t\t-two', + 'end' + ].join('\n')); + + editor.getModel().updateOptions({ insertSpaces: false }); + editor.setSelection(new Selection(2, 2, 3, 7)); + + new SnippetSession(editor, '
\n\t$TM_SELECTED_TEXT\n
$0').insert(); + + let expected = [ + 'start', + '\t
', + '\t\t\t-one', + '\t\t\t-two', + '\t
', + 'end' + ].join('\n'); + + assert.equal(editor.getModel().getValue(), expected); + + editor.getModel().setValue([ + 'start', + '\t\t-one', + '\t-two', + 'end' + ].join('\n')); + + editor.getModel().updateOptions({ insertSpaces: false }); + editor.setSelection(new Selection(2, 2, 3, 7)); + + new SnippetSession(editor, '
\n\t$TM_SELECTED_TEXT\n
$0').insert(); + + expected = [ + 'start', + '\t
', + '\t\t\t-one', + '\t\t-two', + '\t
', + 'end' + ].join('\n'); + + assert.equal(editor.getModel().getValue(), expected); + }); }); diff --git a/src/vs/editor/contrib/snippet/test/browser/snippetVariables.test.ts b/src/vs/editor/contrib/snippet/test/browser/snippetVariables.test.ts index a21516d37d8..07c264b307f 100644 --- a/src/vs/editor/contrib/snippet/test/browser/snippetVariables.test.ts +++ b/src/vs/editor/contrib/snippet/test/browser/snippetVariables.test.ts @@ -31,59 +31,70 @@ suite('Snippet Variables Resolver', function () { model.dispose(); }); + function assertVariableResolve(resolver: EditorSnippetVariableResolver, varName: string, expected: string) { + const snippet = new SnippetParser().parse(`$${varName}`); + const variable = snippet.children[0]; + variable.resolve(resolver); + if (variable.children.length === 0) { + assert.equal(undefined, expected); + } else { + assert.equal(variable.toString(), expected); + } + } + test('editor variables, basics', function () { - assert.equal(resolver.resolve(new Variable('TM_FILENAME')), 'text.txt'); - assert.equal(resolver.resolve(new Variable('something')), undefined); + assertVariableResolve(resolver, 'TM_FILENAME', 'text.txt'); + assertVariableResolve(resolver, 'something', undefined); }); test('editor variables, file/dir', function () { - assert.equal(resolver.resolve(new Variable('TM_FILENAME')), 'text.txt'); + assertVariableResolve(resolver, 'TM_FILENAME', 'text.txt'); if (!isWindows) { - assert.equal(resolver.resolve(new Variable('TM_DIRECTORY')), '/foo/files'); - assert.equal(resolver.resolve(new Variable('TM_FILEPATH')), '/foo/files/text.txt'); + assertVariableResolve(resolver, 'TM_DIRECTORY', '/foo/files'); + assertVariableResolve(resolver, 'TM_FILEPATH', '/foo/files/text.txt'); } resolver = new EditorSnippetVariableResolver( Model.createFromString('', undefined, undefined, URI.parse('http://www.pb.o/abc/def/ghi')), new Selection(1, 1, 1, 1) ); - assert.equal(resolver.resolve(new Variable('TM_FILENAME')), 'ghi'); + assertVariableResolve(resolver, 'TM_FILENAME', 'ghi'); if (!isWindows) { - assert.equal(resolver.resolve(new Variable('TM_DIRECTORY')), '/abc/def'); - assert.equal(resolver.resolve(new Variable('TM_FILEPATH')), '/abc/def/ghi'); + assertVariableResolve(resolver, 'TM_DIRECTORY', '/abc/def'); + assertVariableResolve(resolver, 'TM_FILEPATH', '/abc/def/ghi'); } resolver = new EditorSnippetVariableResolver( Model.createFromString('', undefined, undefined, URI.parse('mem:fff.ts')), new Selection(1, 1, 1, 1) ); - assert.equal(resolver.resolve(new Variable('TM_DIRECTORY')), ''); - assert.equal(resolver.resolve(new Variable('TM_FILEPATH')), 'fff.ts'); + assertVariableResolve(resolver, 'TM_DIRECTORY', ''); + assertVariableResolve(resolver, 'TM_FILEPATH', 'fff.ts'); }); test('editor variables, selection', function () { resolver = new EditorSnippetVariableResolver(model, new Selection(1, 2, 2, 3)); - assert.equal(resolver.resolve(new Variable('TM_SELECTED_TEXT')), 'his is line one\nth'); - assert.equal(resolver.resolve(new Variable('TM_CURRENT_LINE')), 'this is line two'); - assert.equal(resolver.resolve(new Variable('TM_LINE_INDEX')), '1'); - assert.equal(resolver.resolve(new Variable('TM_LINE_NUMBER')), '2'); + assertVariableResolve(resolver, 'TM_SELECTED_TEXT', 'his is line one\nth'); + assertVariableResolve(resolver, 'TM_CURRENT_LINE', 'this is line two'); + assertVariableResolve(resolver, 'TM_LINE_INDEX', '1'); + assertVariableResolve(resolver, 'TM_LINE_NUMBER', '2'); resolver = new EditorSnippetVariableResolver(model, new Selection(2, 3, 1, 2)); - assert.equal(resolver.resolve(new Variable('TM_SELECTED_TEXT')), 'his is line one\nth'); - assert.equal(resolver.resolve(new Variable('TM_CURRENT_LINE')), 'this is line one'); - assert.equal(resolver.resolve(new Variable('TM_LINE_INDEX')), '0'); - assert.equal(resolver.resolve(new Variable('TM_LINE_NUMBER')), '1'); + assertVariableResolve(resolver, 'TM_SELECTED_TEXT', 'his is line one\nth'); + assertVariableResolve(resolver, 'TM_CURRENT_LINE', 'this is line one'); + assertVariableResolve(resolver, 'TM_LINE_INDEX', '0'); + assertVariableResolve(resolver, 'TM_LINE_NUMBER', '1'); resolver = new EditorSnippetVariableResolver(model, new Selection(1, 2, 1, 2)); - assert.equal(resolver.resolve(new Variable('TM_SELECTED_TEXT')), undefined); + assertVariableResolve(resolver, 'TM_SELECTED_TEXT', undefined); - assert.equal(resolver.resolve(new Variable('TM_CURRENT_WORD')), 'this'); + assertVariableResolve(resolver, 'TM_CURRENT_WORD', 'this'); resolver = new EditorSnippetVariableResolver(model, new Selection(3, 1, 3, 1)); - assert.equal(resolver.resolve(new Variable('TM_CURRENT_WORD')), undefined); + assertVariableResolve(resolver, 'TM_CURRENT_WORD', undefined); }); From 269c8e64adf6decc97ff4528bcad3ab36409e957 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Tue, 25 Jul 2017 15:03:50 +0200 Subject: [PATCH 050/136] fix an issue with variable names and snippet rewriting --- .../editor/contrib/snippet/test/browser/snippetParser.test.ts | 2 ++ src/vs/workbench/parts/emmet/electron-browser/editorAccessor.ts | 2 +- src/vs/workbench/parts/snippets/electron-browser/TMSnippets.ts | 2 +- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/vs/editor/contrib/snippet/test/browser/snippetParser.test.ts b/src/vs/editor/contrib/snippet/test/browser/snippetParser.test.ts index a1ffbe2fec4..f0e4cd0c275 100644 --- a/src/vs/editor/contrib/snippet/test/browser/snippetParser.test.ts +++ b/src/vs/editor/contrib/snippet/test/browser/snippetParser.test.ts @@ -197,6 +197,8 @@ suite('SnippetParser', () => { assertTextAndMarker('$123', '', Placeholder); assertTextAndMarker('$farboo', '', Variable); assertTextAndMarker('$far12boo', '', Variable); + assertTextAndMarker('000_${far}_000', '000__000', Text, Variable, Text); + assertTextAndMarker('FFF_${TM_SELECTED_TEXT}_FFF$0', 'FFF__FFF', Text, Variable, Text, Placeholder); }); test('Parser, variables/placeholder with defaults', () => { diff --git a/src/vs/workbench/parts/emmet/electron-browser/editorAccessor.ts b/src/vs/workbench/parts/emmet/electron-browser/editorAccessor.ts index f85914cdebd..073b49440ec 100644 --- a/src/vs/workbench/parts/emmet/electron-browser/editorAccessor.ts +++ b/src/vs/workbench/parts/emmet/electron-browser/editorAccessor.ts @@ -155,7 +155,7 @@ export class EditorAccessor implements emmet.Editor { if (marker.children.length > 0) { return `\${${marker.name}:${marker.children.map(toSnippetString).join('')}}`; } else { - return `\$${marker.name}`; + return `\${${marker.name}}`; } } else { throw new Error('unexpected marker: ' + marker); diff --git a/src/vs/workbench/parts/snippets/electron-browser/TMSnippets.ts b/src/vs/workbench/parts/snippets/electron-browser/TMSnippets.ts index c1e07d5ccd2..3495e4f1770 100644 --- a/src/vs/workbench/parts/snippets/electron-browser/TMSnippets.ts +++ b/src/vs/workbench/parts/snippets/electron-browser/TMSnippets.ts @@ -193,7 +193,7 @@ function _rewriteBogousVariables(snippet: ISnippet): boolean { } else if (marker.children.length > 0) { return `\${${marker.name}:${marker.children.map(fixBogousVariables).join('')}}`; } else { - return `\$${marker.name}`; + return `\${${marker.name}}`; } } else { throw new Error('unexpected marker: ' + marker); From 9f4efe9974cc2fd12a757a721da953190b730321 Mon Sep 17 00:00:00 2001 From: isidor Date: Tue, 25 Jul 2017 16:04:56 +0200 Subject: [PATCH 051/136] debug: multi root debug dropdown --- .../parts/debug/browser/debugActionItems.ts | 47 +++++++++++-------- 1 file changed, 28 insertions(+), 19 deletions(-) diff --git a/src/vs/workbench/parts/debug/browser/debugActionItems.ts b/src/vs/workbench/parts/debug/browser/debugActionItems.ts index 1fe29fe3c03..1f13292c904 100644 --- a/src/vs/workbench/parts/debug/browser/debugActionItems.ts +++ b/src/vs/workbench/parts/debug/browser/debugActionItems.ts @@ -8,6 +8,7 @@ import * as lifecycle from 'vs/base/common/lifecycle'; import * as errors from 'vs/base/common/errors'; import { IAction, IActionRunner } from 'vs/base/common/actions'; import { KeyCode } from 'vs/base/common/keyCodes'; +import * as paths from 'vs/base/common/paths'; import * as dom from 'vs/base/browser/dom'; import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { SelectBox } from 'vs/base/browser/ui/selectBox/selectBox'; @@ -15,7 +16,7 @@ import { SelectActionItem, IActionItem } from 'vs/base/browser/ui/actionbar/acti import { EventEmitter } from 'vs/base/common/eventEmitter'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { ICommandService } from 'vs/platform/commands/common/commands'; -import { IDebugService, ILaunch } from 'vs/workbench/parts/debug/common/debug'; +import { IDebugService } from 'vs/workbench/parts/debug/common/debug'; import { IThemeService } from 'vs/platform/theme/common/themeService'; import { attachSelectBoxStyler, attachStylerCallback } from 'vs/platform/theme/common/styler'; import { SIDE_BAR_BACKGROUND } from 'vs/workbench/common/theme'; @@ -25,14 +26,13 @@ const $ = dom.$; export class StartDebugActionItem extends EventEmitter implements IActionItem { - private static ADD_CONFIGURATION = nls.localize('addConfiguration', "Add Configuration..."); private static SEPARATOR = '─────────'; public actionRunner: IActionRunner; private container: HTMLElement; private start: HTMLElement; private selectBox: SelectBox; - private options: { name: string, launch: ILaunch }[]; + private executeOnSelect: (() => void)[]; private toDispose: lifecycle.IDisposable[]; constructor( @@ -60,12 +60,7 @@ export class StartDebugActionItem extends EventEmitter implements IActionItem { } })); this.toDispose.push(this.selectBox.onDidSelect(e => { - if (e.selected === StartDebugActionItem.ADD_CONFIGURATION) { - this.selectBox.select(0); - this.commandService.executeCommand('debug.addConfiguration').done(undefined, errors.onUnexpectedError); - } else { - this.debugService.getConfigurationManager().selectConfiguration(this.options[e.index].launch, this.options[e.index].name); - } + this.executeOnSelect[e.index](); })); this.toDispose.push(this.debugService.getConfigurationManager().onDidSelectConfiguration(() => { this.updateOptions(); @@ -150,22 +145,36 @@ export class StartDebugActionItem extends EventEmitter implements IActionItem { private updateOptions(): void { let selected = 0; - this.options = []; - this.debugService.getConfigurationManager().getLaunches().forEach(launch => { + this.executeOnSelect = []; + const options = []; + const launches = this.debugService.getConfigurationManager().getLaunches(); + launches.forEach(launch => { + const launchName = paths.basename(launch.workspaceUri.fsPath); launch.getConfigurationNames().forEach(name => { if (name === this.debugService.getConfigurationManager().selectedName && launch === this.debugService.getConfigurationManager().selectedLaunch) { - selected = this.options.length; + selected = this.executeOnSelect.length; } - this.options.push({ name, launch }); + this.executeOnSelect.push(() => this.debugService.getConfigurationManager().selectConfiguration(launch, name)); + options.push(launches.length > 1 ? `${name} (${launchName})` : name); }); }); - const selectOptions = this.options.map(o => o.name); - if (this.options.length === 0) { - selectOptions.push(nls.localize('noConfigurations', "No Configurations")); + + if (options.length === 0) { + options.push(nls.localize('noConfigurations', "No Configurations")); } - selectOptions.push(StartDebugActionItem.SEPARATOR); - selectOptions.push(StartDebugActionItem.ADD_CONFIGURATION); - this.selectBox.setOptions(selectOptions, selected, selectOptions.length - 2); + options.push(StartDebugActionItem.SEPARATOR); + this.executeOnSelect.push(undefined); + + const disabledIdx = options.length - 1; + launches.forEach(l => { + options.push(launches.length > 1 ? nls.localize("addConfigTo", `Add Config (${paths.basename(l.workspaceUri.fsPath)})...`) : nls.localize('addConfiguration', "Add Configuration...")); + this.executeOnSelect.push(() => { + this.debugService.getConfigurationManager().selectConfiguration(l); + this.commandService.executeCommand('debug.addConfiguration').done(undefined, errors.onUnexpectedError); + }); + }); + + this.selectBox.setOptions(options, selected, disabledIdx); } } From 835da5309ad762ab9b81023c3672e1369c5c3aa6 Mon Sep 17 00:00:00 2001 From: isidor Date: Tue, 25 Jul 2017 16:21:03 +0200 Subject: [PATCH 052/136] debug: do not resolve program and runtime if there is no root --- src/vs/workbench/parts/debug/node/debugAdapter.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/parts/debug/node/debugAdapter.ts b/src/vs/workbench/parts/debug/node/debugAdapter.ts index 9b656fbd65f..2b73c57cb7e 100644 --- a/src/vs/workbench/parts/debug/node/debugAdapter.ts +++ b/src/vs/workbench/parts/debug/node/debugAdapter.ts @@ -85,7 +85,7 @@ export class Adapter { private getRuntime(root: uri): string { let runtime = this.getAttributeBasedOnPlatform('runtime'); if (runtime && runtime.indexOf('./') === 0) { - runtime = this.configurationResolverService ? this.configurationResolverService.resolve(root, runtime) : runtime; + runtime = root ? this.configurationResolverService.resolve(root, runtime) : runtime; runtime = paths.join(this.extensionDescription.extensionFolderPath, runtime); } return runtime; @@ -94,7 +94,7 @@ export class Adapter { private getProgram(root: uri): string { let program = this.getAttributeBasedOnPlatform('program'); if (program) { - program = this.configurationResolverService ? this.configurationResolverService.resolve(root, program) : program; + program = root ? this.configurationResolverService.resolve(root, program) : program; program = paths.join(this.extensionDescription.extensionFolderPath, program); } return program; From 4ddda54c6a720a0d92cc89b95268e13ec45ba587 Mon Sep 17 00:00:00 2001 From: isidor Date: Tue, 25 Jul 2017 16:32:14 +0200 Subject: [PATCH 053/136] debug: configuraiton manager check name before selecting it --- .../debug/electron-browser/debugConfigurationManager.ts | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/parts/debug/electron-browser/debugConfigurationManager.ts b/src/vs/workbench/parts/debug/electron-browser/debugConfigurationManager.ts index b8ae42b7d62..73fe9ae0cdf 100644 --- a/src/vs/workbench/parts/debug/electron-browser/debugConfigurationManager.ts +++ b/src/vs/workbench/parts/debug/electron-browser/debugConfigurationManager.ts @@ -281,6 +281,7 @@ export class ConfigurationManager implements IConfigurationManager { }); this.toDispose.push(this.contextService.onDidChangeWorkspaceRoots(() => { + this.initLaunches(); })); this.toDispose.push(this.configurationService.onDidUpdateConfiguration((event) => { @@ -313,12 +314,16 @@ export class ConfigurationManager implements IConfigurationManager { public selectConfiguration(launch: ILaunch, name?: string): void { this._selectedLaunch = launch; - if (name) { + const names = launch ? launch.getConfigurationNames() : []; + if (name && names.indexOf(name) >= 0) { this._selectedName = name; } + if (names.indexOf(this.selectedName) === -1) { + this._selectedName = names.length ? names[0] : undefined; + } this.storageService.store(DEBUG_SELECTED_CONFIG_NAME_KEY, this.selectedName, StorageScope.WORKSPACE); if (launch) { - this.storageService.store(DEBUG_SELECTED_ROOT, this.contextService.getRoot(launch.uri).toString(), StorageScope.WORKSPACE); + this.storageService.store(DEBUG_SELECTED_ROOT, launch.workspaceUri.toString(), StorageScope.WORKSPACE); } this._onDidSelectConfigurationName.fire(); } From 13cd8ca47722573b6067965cdc3ef3582dcf27e2 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Tue, 25 Jul 2017 20:02:27 +0530 Subject: [PATCH 054/136] Fix #31037 --- .../preferences/browser/media/preferences.css | 2 -- .../browser/preferencesRenderers.ts | 27 ++++++++++++++----- .../preferences/browser/preferencesService.ts | 19 +++---------- .../preferences/browser/preferencesWidgets.ts | 10 ++++++- .../preferences/common/preferencesModels.ts | 2 +- 5 files changed, 35 insertions(+), 25 deletions(-) diff --git a/src/vs/workbench/parts/preferences/browser/media/preferences.css b/src/vs/workbench/parts/preferences/browser/media/preferences.css index eee39c73482..fb63ef3b79d 100644 --- a/src/vs/workbench/parts/preferences/browser/media/preferences.css +++ b/src/vs/workbench/parts/preferences/browser/media/preferences.css @@ -107,7 +107,6 @@ } .monaco-editor .settings-header-widget .title-container { - padding-left: 12px; display: flex; } @@ -128,7 +127,6 @@ } .monaco-editor .settings-header-widget .title-container .message { - padding-left: 12px; white-space: nowrap; } diff --git a/src/vs/workbench/parts/preferences/browser/preferencesRenderers.ts b/src/vs/workbench/parts/preferences/browser/preferencesRenderers.ts index 0756292a4a2..a274ede5bf9 100644 --- a/src/vs/workbench/parts/preferences/browser/preferencesRenderers.ts +++ b/src/vs/workbench/parts/preferences/browser/preferencesRenderers.ts @@ -82,6 +82,12 @@ export class UserSettingsRenderer extends Disposable implements IPreferencesRend this.editSettingActionRenderer = this._register(this.instantiationService.createInstance(EditSettingRenderer, this.editor, this.preferencesModel, this.settingHighlighter)); this._register(this.editSettingActionRenderer.onUpdateSetting(({ key, value, source }) => this.updatePreference(key, value, source))); this._register(this.editor.getModel().onDidChangeContent(() => this.modelChangeDelayer.trigger(() => this.onModelChanged()))); + + this.createHeader(); + } + + protected createHeader(): void { + this._register(new SettingsHeaderWidget(this.editor, '')).setMessage(nls.localize('emptyUserSettingsHeader', "Place your settings here to overwrite the Default Settings.")); } public render(): void { @@ -178,6 +184,10 @@ export class WorkspaceSettingsRenderer extends UserSettingsRenderer implements I this.untrustedSettingRenderer = this._register(instantiationService.createInstance(UnsupportedWorkspaceSettingsRenderer, editor, preferencesModel)); } + protected createHeader(): void { + this._register(new SettingsHeaderWidget(this.editor, '')).setMessage(nls.localize('emptyWorkspaceSettingsHeader', "Place your settings here to overwrite the User Settings.")); + } + public render(): void { super.render(); this.untrustedSettingRenderer.render(); @@ -200,6 +210,10 @@ export class FolderSettingsRenderer extends UserSettingsRenderer implements IPre this.unsupportedWorkbenchSettingsRenderer = this._register(instantiationService.createInstance(UnsupportedWorkbenchSettingsRenderer, editor, preferencesModel)); } + protected createHeader(): void { + this._register(new SettingsHeaderWidget(this.editor, '')).setMessage(nls.localize('emptyFolderSettingsHeader', "Place your folder settings here to overwrite those from the Workspace Settings.")); + } + public render(): void { super.render(); this.unsupportedWorkbenchSettingsRenderer.render(); @@ -210,7 +224,7 @@ export class DefaultSettingsRenderer extends Disposable implements IPreferencesR private _associatedPreferencesModel: IPreferencesEditorModel; private settingHighlighter: SettingHighlighter; - private settingsHeaderRenderer: SettingsHeaderRenderer; + private settingsHeaderRenderer: DefaultSettingsHeaderRenderer; private settingsGroupTitleRenderer: SettingsGroupTitleRenderer; private filteredMatchesRenderer: FilteredMatchesRenderer; private hiddenAreasRenderer: HiddenAreasRenderer; @@ -234,7 +248,7 @@ export class DefaultSettingsRenderer extends Disposable implements IPreferencesR ) { super(); this.settingHighlighter = this._register(instantiationService.createInstance(SettingHighlighter, editor, this._onFocusPreference, this._onClearFocusPreference)); - this.settingsHeaderRenderer = this._register(instantiationService.createInstance(SettingsHeaderRenderer, editor)); + this.settingsHeaderRenderer = this._register(instantiationService.createInstance(DefaultSettingsHeaderRenderer, editor, preferencesModel.configurationScope)); 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)); @@ -369,18 +383,19 @@ export class StaticContentHidingRenderer extends Disposable implements HiddenAre } -class SettingsHeaderRenderer extends Disposable { +class DefaultSettingsHeaderRenderer extends Disposable { private settingsHeaderWidget: SettingsHeaderWidget; - constructor(private editor: ICodeEditor) { + constructor(private editor: ICodeEditor, scope: ConfigurationScope) { super(); - this.settingsHeaderWidget = this._register(new SettingsHeaderWidget(editor, nls.localize('defaultSettingsTitle', "Default Settings"))); + const title = scope === ConfigurationScope.RESOURCE ? nls.localize('defaultFolderSettingsTitle', "Default Folder Settings") : nls.localize('defaultSettingsTitle', "Default Settings"); + this.settingsHeaderWidget = this._register(new SettingsHeaderWidget(editor, title)); } public render(settingsGroups: ISettingsGroup[]) { if (settingsGroups.length) { - this.settingsHeaderWidget.setMessage(nls.localize('defaultSettingsMessage', "Place your settings in the file to the right to overwrite.")); + this.settingsHeaderWidget.setMessage(''); } else { this.settingsHeaderWidget.setMessage(nls.localize('noSettingsFound', "No Settings Found.")); } diff --git a/src/vs/workbench/parts/preferences/browser/preferencesService.ts b/src/vs/workbench/parts/preferences/browser/preferencesService.ts index 8fffe6ed7ce..e412ad5316a 100644 --- a/src/vs/workbench/parts/preferences/browser/preferencesService.ts +++ b/src/vs/workbench/parts/preferences/browser/preferencesService.ts @@ -51,6 +51,8 @@ interface IWorkbenchSettingsConfiguration { }; } +const emptyEditableSettingsContent = '{\n}'; + export class PreferencesService extends Disposable implements IPreferencesService { _serviceBrand: any; @@ -299,24 +301,12 @@ export class PreferencesService extends Disposable implements IPreferencesServic const model = reference.object.textEditorModel; const settingsContent = WorkspaceConfigModel.getSettingsContentFromConfigContent(model.getValue()); reference.dispose(); - return TPromise.as(settingsContent ? settingsContent : this.getEmptyEditableSettingsContent(ConfigurationTarget.WORKSPACE)); + return TPromise.as(settingsContent ? settingsContent : emptyEditableSettingsContent); }); } return TPromise.as(null); } - private getEmptyEditableSettingsContent(target: ConfigurationTarget): string { - if (target === ConfigurationTarget.USER) { - const emptySettingsHeader = nls.localize('emptySettingsHeader', "Place your settings in this file to overwrite the default settings"); - return '// ' + emptySettingsHeader + '\n{\n}'; - } - return [ - '// ' + nls.localize('emptySettingsHeader1', "Place your settings in this file to overwrite default and user settings."), - '{', - '}' - ].join('\n'); - } - private getEditableSettingsURI(configurationTarget: ConfigurationTarget, resource?: URI): URI { switch (configurationTarget) { case ConfigurationTarget.USER: @@ -345,8 +335,7 @@ export class PreferencesService extends Disposable implements IPreferencesServic if (this.contextService.hasMultiFolderWorkspace() && target === ConfigurationTarget.WORKSPACE) { return TPromise.as(null); } - const editableSettingsEmptyContent = this.getEmptyEditableSettingsContent(target); - return this.createIfNotExists(resource, editableSettingsEmptyContent).then(() => { }); + return this.createIfNotExists(resource, emptyEditableSettingsContent).then(() => { }); } private createIfNotExists(resource: URI, contents: string): TPromise { diff --git a/src/vs/workbench/parts/preferences/browser/preferencesWidgets.ts b/src/vs/workbench/parts/preferences/browser/preferencesWidgets.ts index bce0560ebaa..26e0aa5012a 100644 --- a/src/vs/workbench/parts/preferences/browser/preferencesWidgets.ts +++ b/src/vs/workbench/parts/preferences/browser/preferencesWidgets.ts @@ -66,8 +66,13 @@ export class SettingsHeaderWidget extends Widget implements IViewZone { this._domNode = DOM.$('.settings-header-widget'); this.titleContainer = DOM.append(this._domNode, DOM.$('.title-container')); - DOM.append(this.titleContainer, DOM.$('.title')).textContent = this.title; + if (this.title) { + DOM.append(this.titleContainer, DOM.$('.title')).textContent = this.title; + } this.messageElement = DOM.append(this.titleContainer, DOM.$('.message')); + if (this.title) { + this.messageElement.style.paddingLeft = '12px'; + } this.editor.changeViewZones(accessor => { this.id = accessor.addZone(this); @@ -82,6 +87,9 @@ export class SettingsHeaderWidget extends Widget implements IViewZone { private layout(): void { const configuration = this.editor.getConfiguration(); this.titleContainer.style.fontSize = configuration.fontInfo.fontSize + 'px'; + if (!configuration.contribInfo.folding) { + this.titleContainer.style.paddingLeft = '12px'; + } } public dispose() { diff --git a/src/vs/workbench/parts/preferences/common/preferencesModels.ts b/src/vs/workbench/parts/preferences/common/preferencesModels.ts index 785707f7093..4cc3705f767 100644 --- a/src/vs/workbench/parts/preferences/common/preferencesModels.ts +++ b/src/vs/workbench/parts/preferences/common/preferencesModels.ts @@ -598,7 +598,7 @@ export class DefaultSettingsEditorModel extends AbstractSettingsModel implements private _content: string; private _contentByLines: string[]; - constructor(private _uri: URI, private _mostCommonlyUsedSettingsKeys: string[], private configurationScope: ConfigurationScope) { + constructor(private _uri: URI, private _mostCommonlyUsedSettingsKeys: string[], readonly configurationScope: ConfigurationScope) { super(); } From 5a622a9967b8589363470efdeb075b15d972af4e Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Tue, 25 Jul 2017 20:13:45 +0530 Subject: [PATCH 055/136] Implemente feedback --- src/vs/vscode.d.ts | 20 ++++++------ .../api/node/extHostConfiguration.ts | 6 ++-- .../api/extHostConfiguration.test.ts | 32 +++++++++---------- 3 files changed, 29 insertions(+), 29 deletions(-) diff --git a/src/vs/vscode.d.ts b/src/vs/vscode.d.ts index c880739c315..16f71db579a 100644 --- a/src/vs/vscode.d.ts +++ b/src/vs/vscode.d.ts @@ -2978,11 +2978,11 @@ declare module 'vscode' { * - Workspace configuration (if available) * - Workspace folder configuration of the requested resource (if available) * - * **Global configuration** comes from User Settings and shadows Defaults. + * *Global configuration* comes from User Settings and shadows Defaults. * - * **Workspace configuration** comes from Workspace Settings and shadows Global configuration. + * *Workspace configuration* comes from Workspace Settings and shadows Global configuration. * - * **Workspace Folder configuration** comes from `.vscode` folder under one of the [workspace folders](#workspace.workspaceFolders). + * *Workspace Folder configuration* comes from `.vscode` folder under one of the [workspace folders](#workspace.workspaceFolders). * * *Note:* Workspace and Workspace Folder configurations contains `launch` and `tasks` settings. Their basename will be * part of the section identifier. The following snippets shows how to retrieve all configurations @@ -2990,7 +2990,7 @@ declare module 'vscode' { * * ```ts * // launch.json configuration - * const config = workspace.getConfiguration(workspace.workspaceFolders[1], 'launch'); + * const config = workspace.getConfiguration('launch', vscode.window.activeTextEditor.document.uri); * * // retrieve values * const values = config.get('configurations'); @@ -3032,7 +3032,7 @@ declare module 'vscode' { * * The *effective* value (returned by [`get`](#WorkspaceConfiguration.get)) * is computed like this: `defaultValue` overwritten by `globalValue`, - * `globalValue` overwritten by `workspaceValue`. `workspaceValue` overwritten by `folderValue`. + * `globalValue` overwritten by `workspaceValue`. `workspaceValue` overwritten by `workspaceFolderValue`. * Refer to [Settings Inheritence](https://code.visualstudio.com/docs/getstarted/settings) * for more information. * @@ -3042,7 +3042,7 @@ declare module 'vscode' { * @param section Configuration name, supports _dotted_ names. * @return Information about a configuration setting or `undefined`. */ - inspect(section: string): { key: string; defaultValue?: T; globalValue?: T; workspaceValue?: T, folderValue?: T } | undefined; + inspect(section: string): { key: string; defaultValue?: T; globalValue?: T; workspaceValue?: T, workspaceFolderValue?: T } | undefined; /** * Update a configuration value. The updated configuration values are persisted. @@ -3064,11 +3064,11 @@ declare module 'vscode' { * * @param section Configuration name, supports _dotted_ names. * @param value The new value. - * @param configurationTarget The [configuration target](#ConfigurationTarget) - * @param global When `true` changes the global configuration value otherwise workspace configuration value + * @param configurationTarget The [configuration target](#ConfigurationTarget) or a boolean value. + * If `undefined` or `null` or `false` configuration target is `ConfigurationTarget.Workspace`. + * If `true` configuration target is `ConfigurationTarget.Global`. */ - update(section: string, value: any, configurationTarget: ConfigurationTarget): Thenable; - update(section: string, value: any, global?: boolean): Thenable; + update(section: string, value: any, configurationTarget?: ConfigurationTarget | boolean): Thenable; /** * Readable dictionary that backs this configuration. diff --git a/src/vs/workbench/api/node/extHostConfiguration.ts b/src/vs/workbench/api/node/extHostConfiguration.ts index 77e42189cc6..9cb6406ce2d 100644 --- a/src/vs/workbench/api/node/extHostConfiguration.ts +++ b/src/vs/workbench/api/node/extHostConfiguration.ts @@ -30,7 +30,7 @@ type ConfigurationInspect = { defaultValue?: T; globalValue?: T; workspaceValue?: T; - folderValue?: T; + workspaceFolderValue?: T; }; export class ExtHostConfiguration extends ExtHostConfigurationShape { @@ -87,7 +87,7 @@ export class ExtHostConfiguration extends ExtHostConfigurationShape { } return result; }, - update: (key: string, value: any, arg: boolean | ExtHostConfigurationTarget) => { + update: (key: string, value: any, arg: ExtHostConfigurationTarget | boolean) => { key = section ? `${section}.${key}` : key; const target = parseConfigurationTarget(arg); if (value !== void 0) { @@ -105,7 +105,7 @@ export class ExtHostConfiguration extends ExtHostConfigurationShape { defaultValue: config.default, globalValue: config.user, workspaceValue: config.workspace, - folderValue: config.folder + workspaceFolderValue: config.folder }; } return undefined; diff --git a/src/vs/workbench/test/electron-browser/api/extHostConfiguration.test.ts b/src/vs/workbench/test/electron-browser/api/extHostConfiguration.test.ts index accbd651fc6..8a072c4214a 100644 --- a/src/vs/workbench/test/electron-browser/api/extHostConfiguration.test.ts +++ b/src/vs/workbench/test/electron-browser/api/extHostConfiguration.test.ts @@ -108,13 +108,13 @@ suite('ExtHostConfiguration', function () { assert.equal(actual.defaultValue, 'off'); assert.equal(actual.globalValue, 'on'); assert.equal(actual.workspaceValue, undefined); - assert.equal(actual.folderValue, undefined); + assert.equal(actual.workspaceFolderValue, undefined); actual = testObject.getConfiguration('editor').inspect('wordWrap'); assert.equal(actual.defaultValue, 'off'); assert.equal(actual.globalValue, 'on'); assert.equal(actual.workspaceValue, undefined); - assert.equal(actual.folderValue, undefined); + assert.equal(actual.workspaceFolderValue, undefined); }); test('inspect in single root context', function () { @@ -153,25 +153,25 @@ suite('ExtHostConfiguration', function () { assert.equal(actual1.defaultValue, 'off'); assert.equal(actual1.globalValue, 'on'); assert.equal(actual1.workspaceValue, 'bounded'); - assert.equal(actual1.folderValue, undefined); + assert.equal(actual1.workspaceFolderValue, undefined); actual1 = testObject.getConfiguration('editor').inspect('wordWrap'); assert.equal(actual1.defaultValue, 'off'); assert.equal(actual1.globalValue, 'on'); assert.equal(actual1.workspaceValue, 'bounded'); - assert.equal(actual1.folderValue, undefined); + assert.equal(actual1.workspaceFolderValue, undefined); let actual2 = testObject.getConfiguration(null, workspaceUri).inspect('editor.wordWrap'); assert.equal(actual2.defaultValue, 'off'); assert.equal(actual2.globalValue, 'on'); assert.equal(actual2.workspaceValue, 'bounded'); - assert.equal(actual2.folderValue, 'bounded'); + assert.equal(actual2.workspaceFolderValue, 'bounded'); actual2 = testObject.getConfiguration('editor', workspaceUri).inspect('wordWrap'); assert.equal(actual2.defaultValue, 'off'); assert.equal(actual2.globalValue, 'on'); assert.equal(actual2.workspaceValue, 'bounded'); - assert.equal(actual2.folderValue, 'bounded'); + assert.equal(actual2.workspaceFolderValue, 'bounded'); }); test('inspect in multi root context', function () { @@ -226,63 +226,63 @@ suite('ExtHostConfiguration', function () { assert.equal(actual1.defaultValue, 'off'); assert.equal(actual1.globalValue, 'on'); assert.equal(actual1.workspaceValue, 'bounded'); - assert.equal(actual1.folderValue, undefined); + assert.equal(actual1.workspaceFolderValue, undefined); actual1 = testObject.getConfiguration('editor').inspect('wordWrap'); assert.equal(actual1.defaultValue, 'off'); assert.equal(actual1.globalValue, 'on'); assert.equal(actual1.workspaceValue, 'bounded'); - assert.equal(actual1.folderValue, undefined); + assert.equal(actual1.workspaceFolderValue, undefined); actual1 = testObject.getConfiguration('editor').inspect('lineNumbers'); assert.equal(actual1.defaultValue, 'on'); assert.equal(actual1.globalValue, undefined); assert.equal(actual1.workspaceValue, undefined); - assert.equal(actual1.folderValue, undefined); + assert.equal(actual1.workspaceFolderValue, undefined); let actual2 = testObject.getConfiguration(null, firstRoot).inspect('editor.wordWrap'); assert.equal(actual2.defaultValue, 'off'); assert.equal(actual2.globalValue, 'on'); assert.equal(actual2.workspaceValue, 'bounded'); - assert.equal(actual2.folderValue, 'off'); + assert.equal(actual2.workspaceFolderValue, 'off'); actual2 = testObject.getConfiguration('editor', firstRoot).inspect('wordWrap'); assert.equal(actual2.defaultValue, 'off'); assert.equal(actual2.globalValue, 'on'); assert.equal(actual2.workspaceValue, 'bounded'); - assert.equal(actual2.folderValue, 'off'); + assert.equal(actual2.workspaceFolderValue, 'off'); actual2 = testObject.getConfiguration('editor', firstRoot).inspect('lineNumbers'); assert.equal(actual2.defaultValue, 'on'); assert.equal(actual2.globalValue, undefined); assert.equal(actual2.workspaceValue, undefined); - assert.equal(actual2.folderValue, 'relative'); + assert.equal(actual2.workspaceFolderValue, 'relative'); actual2 = testObject.getConfiguration(null, secondRoot).inspect('editor.wordWrap'); assert.equal(actual2.defaultValue, 'off'); assert.equal(actual2.globalValue, 'on'); assert.equal(actual2.workspaceValue, 'bounded'); - assert.equal(actual2.folderValue, 'on'); + assert.equal(actual2.workspaceFolderValue, 'on'); actual2 = testObject.getConfiguration('editor', secondRoot).inspect('wordWrap'); assert.equal(actual2.defaultValue, 'off'); assert.equal(actual2.globalValue, 'on'); assert.equal(actual2.workspaceValue, 'bounded'); - assert.equal(actual2.folderValue, 'on'); + assert.equal(actual2.workspaceFolderValue, 'on'); actual2 = testObject.getConfiguration(null, thirdRoot).inspect('editor.wordWrap'); assert.equal(actual2.defaultValue, 'off'); assert.equal(actual2.globalValue, 'on'); assert.equal(actual2.workspaceValue, 'bounded'); assert.ok(Object.keys(actual2).indexOf('folderValue') !== -1); - assert.equal(actual2.folderValue, undefined); + assert.equal(actual2.workspaceFolderValue, undefined); actual2 = testObject.getConfiguration('editor', thirdRoot).inspect('wordWrap'); assert.equal(actual2.defaultValue, 'off'); assert.equal(actual2.globalValue, 'on'); assert.equal(actual2.workspaceValue, 'bounded'); assert.ok(Object.keys(actual2).indexOf('folderValue') !== -1); - assert.equal(actual2.folderValue, undefined); + assert.equal(actual2.workspaceFolderValue, undefined); }); test('getConfiguration vs get', function () { From e2bdb3265a3fffeeca50d46bddbff9adb47f4065 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Tue, 25 Jul 2017 20:21:00 +0530 Subject: [PATCH 056/136] Fix tests --- .../test/electron-browser/api/extHostConfiguration.test.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/test/electron-browser/api/extHostConfiguration.test.ts b/src/vs/workbench/test/electron-browser/api/extHostConfiguration.test.ts index 8a072c4214a..68578048215 100644 --- a/src/vs/workbench/test/electron-browser/api/extHostConfiguration.test.ts +++ b/src/vs/workbench/test/electron-browser/api/extHostConfiguration.test.ts @@ -274,14 +274,14 @@ suite('ExtHostConfiguration', function () { assert.equal(actual2.defaultValue, 'off'); assert.equal(actual2.globalValue, 'on'); assert.equal(actual2.workspaceValue, 'bounded'); - assert.ok(Object.keys(actual2).indexOf('folderValue') !== -1); + assert.ok(Object.keys(actual2).indexOf('workspaceFolderValue') !== -1); assert.equal(actual2.workspaceFolderValue, undefined); actual2 = testObject.getConfiguration('editor', thirdRoot).inspect('wordWrap'); assert.equal(actual2.defaultValue, 'off'); assert.equal(actual2.globalValue, 'on'); assert.equal(actual2.workspaceValue, 'bounded'); - assert.ok(Object.keys(actual2).indexOf('folderValue') !== -1); + assert.ok(Object.keys(actual2).indexOf('workspaceFolderValue') !== -1); assert.equal(actual2.workspaceFolderValue, undefined); }); From 5077d2f22bedfba5d8d3d883a483b4099ebd69f4 Mon Sep 17 00:00:00 2001 From: isidor Date: Tue, 25 Jul 2017 17:05:31 +0200 Subject: [PATCH 057/136] debug: varios todos --- .../debug/electron-browser/debugConfigurationManager.ts | 8 ++++---- .../parts/debug/electron-browser/debugService.ts | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/vs/workbench/parts/debug/electron-browser/debugConfigurationManager.ts b/src/vs/workbench/parts/debug/electron-browser/debugConfigurationManager.ts index 73fe9ae0cdf..98c4e86c020 100644 --- a/src/vs/workbench/parts/debug/electron-browser/debugConfigurationManager.ts +++ b/src/vs/workbench/parts/debug/electron-browser/debugConfigurationManager.ts @@ -232,8 +232,8 @@ export class ConfigurationManager implements IConfigurationManager { this.toDispose = []; this.registerListeners(); this.initLaunches(); - // TODO@isidor select the appropriate Launch - this.selectConfiguration(this.launches.length ? this.launches[0] : undefined, this.storageService.get(DEBUG_SELECTED_CONFIG_NAME_KEY, StorageScope.WORKSPACE, null)); + const previousSelectedRoot = this.storageService.get(DEBUG_SELECTED_ROOT, StorageScope.WORKSPACE); + this.selectConfiguration(this.launches.filter(l => l.workspaceUri.toString() === previousSelectedRoot).pop(), this.storageService.get(DEBUG_SELECTED_CONFIG_NAME_KEY, StorageScope.WORKSPACE)); } private registerListeners(): void { @@ -285,8 +285,8 @@ export class ConfigurationManager implements IConfigurationManager { })); this.toDispose.push(this.configurationService.onDidUpdateConfiguration((event) => { - if (event.sourceConfig) { - // TODO@Isidor react on user changing the launch.json + if (event.sourceConfig.launch) { + this.selectConfiguration(this.selectedLaunch); } })); } diff --git a/src/vs/workbench/parts/debug/electron-browser/debugService.ts b/src/vs/workbench/parts/debug/electron-browser/debugService.ts index e3443bb537c..0f833ce7ab8 100644 --- a/src/vs/workbench/parts/debug/electron-browser/debugService.ts +++ b/src/vs/workbench/parts/debug/electron-browser/debugService.ts @@ -869,7 +869,7 @@ export class DebugService implements debug.IDebugService { watchExpressionsCount: this.model.getWatchExpressions().length, extensionName: `${adapter.extensionDescription.publisher}.${adapter.extensionDescription.name}`, isBuiltin: adapter.extensionDescription.isBuiltin, - launchJsonExists: this.contextService.hasWorkspace() && !!this.configurationService.getConfiguration('launch', { resource: this.contextService.getLegacyWorkspace().resource }) // TODO@Isidor (https://github.com/Microsoft/vscode/issues/29245) + launchJsonExists: this.contextService.hasWorkspace() && !!this.configurationService.getConfiguration('launch', { resource: root }) }); }).then(() => process, (error: any) => { if (error instanceof Error && error.message === 'Canceled') { From 2b61318d42a2b9cbd48a9172ab445d4daa68bc76 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Tue, 25 Jul 2017 20:49:56 +0530 Subject: [PATCH 058/136] #31246 Revert changes - Revert auto saving settings - Revert virtual editor for workspace settings in MR workspace --- .../preferences/browser/preferencesEditor.ts | 41 ++++++++++++------- .../preferences/browser/preferencesService.ts | 6 ++- 2 files changed, 30 insertions(+), 17 deletions(-) diff --git a/src/vs/workbench/parts/preferences/browser/preferencesEditor.ts b/src/vs/workbench/parts/preferences/browser/preferencesEditor.ts index d75bab0ad35..d09b8f026b9 100644 --- a/src/vs/workbench/parts/preferences/browser/preferencesEditor.ts +++ b/src/vs/workbench/parts/preferences/browser/preferencesEditor.ts @@ -12,8 +12,8 @@ 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 } from 'vs/workbench/common/editor'; -import { BaseEditor } from 'vs/workbench/browser/parts/editor/baseEditor'; +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 { 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'; @@ -58,6 +58,7 @@ import { attachStylerCallback } from 'vs/platform/theme/common/styler'; 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"; export class PreferencesEditorInput extends SideBySideEditorInput { public static ID: string = 'workbench.editorinputs.preferencesEditorInput'; @@ -69,10 +70,6 @@ export class PreferencesEditorInput extends SideBySideEditorInput { public getTitle(verbosity: Verbosity): string { return this.master.getTitle(verbosity); } - - public isDirty(): boolean { - return false; - } } export class DefaultPreferencesEditorInput extends ResourceEditorInput { @@ -416,7 +413,7 @@ class SideBySidePreferencesWidget extends Widget { private dimension: Dimension; private defaultPreferencesEditor: DefaultPreferencesEditor; - private editablePreferencesEditor: EditableSettingsEditor; + private editablePreferencesEditor: BaseEditor; private defaultPreferencesEditorContainer: HTMLElement; private editablePreferencesEditorContainer: HTMLElement; @@ -445,10 +442,6 @@ class SideBySidePreferencesWidget extends Widget { this.editablePreferencesEditorContainer = DOM.append(parentElement, DOM.$('.editable-preferences-editor-container')); this.editablePreferencesEditorContainer.style.position = 'absolute'; - this.editablePreferencesEditor = this._register(this.instantiationService.createInstance(EditableSettingsEditor)); - this.editablePreferencesEditor.create(new Builder(this.editablePreferencesEditorContainer)); - this.editablePreferencesEditor.setVisible(true); - (this.editablePreferencesEditor.getControl()).onDidFocusEditor(() => this.lastFocusedEditor = this.editablePreferencesEditor); this.lastFocusedEditor = this.editablePreferencesEditor; this._register(attachStylerCallback(this.themeService, { scrollbarShadow }, colors => { @@ -466,10 +459,13 @@ class SideBySidePreferencesWidget extends Widget { } public setInput(defaultPreferencesEditorInput: DefaultPreferencesEditorInput, editablePreferencesEditorInput: EditorInput, options?: EditorOptions): TPromise<{ defaultPreferencesRenderer: IPreferencesRenderer, editablePreferencesRenderer: IPreferencesRenderer }> { - 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 })); + 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 })); + }); } public layout(dimension: Dimension): void { @@ -505,6 +501,21 @@ class SideBySidePreferencesWidget extends Widget { } } + private getOrCreateEditablePreferencesEditor(editorInput: EditorInput): TPromise { + if (this.editablePreferencesEditor) { + return TPromise.as(this.editablePreferencesEditor); + } + const descriptor = Registry.as(EditorExtensions.Editors).getEditor(editorInput); + return this.instantiationService.createInstance(descriptor) + .then((editor: BaseEditor) => { + this.editablePreferencesEditor = editor; + this.editablePreferencesEditor.create(new Builder(this.editablePreferencesEditorContainer)); + this.editablePreferencesEditor.setVisible(true); + (this.editablePreferencesEditor.getControl()).onDidFocusEditor(() => this.lastFocusedEditor = this.editablePreferencesEditor); + return editor; + }); + } + private updateInput(editor: BaseEditor, input: EditorInput, editorContributionId: string, associatedPreferencesModelUri: URI, options: EditorOptions): TPromise> { return editor.setInput(input, options) .then(() => (editor.getControl()).getContribution(editorContributionId).createPreferencesRenderer(associatedPreferencesModelUri)); diff --git a/src/vs/workbench/parts/preferences/browser/preferencesService.ts b/src/vs/workbench/parts/preferences/browser/preferencesService.ts index e412ad5316a..e957b4c3164 100644 --- a/src/vs/workbench/parts/preferences/browser/preferencesService.ts +++ b/src/vs/workbench/parts/preferences/browser/preferencesService.ts @@ -317,7 +317,7 @@ export class PreferencesService extends Disposable implements IPreferencesServic return this.toResource(paths.join('.vscode', 'settings.json'), workspace.roots[0]); } if (this.contextService.hasMultiFolderWorkspace()) { - return this.workspaceConfigSettingsResource; + return workspace.configuration; } return null; case ConfigurationTarget.FOLDER: @@ -333,7 +333,9 @@ export class PreferencesService extends Disposable implements IPreferencesServic private createSettingsIfNotExists(target: ConfigurationTarget, resource: URI): TPromise { if (this.contextService.hasMultiFolderWorkspace() && target === ConfigurationTarget.WORKSPACE) { - return TPromise.as(null); + if (!this.configurationService.keys().workspace.length) { + return this.jsonEditingService.write(resource, { key: 'settings', value: {} }, true).then(null, () => { }); + } } return this.createIfNotExists(resource, emptyEditableSettingsContent).then(() => { }); } From 8df78c5cfcae10cd34c39c8fc770dd1dfea3cc3a Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Tue, 25 Jul 2017 22:25:21 +0530 Subject: [PATCH 059/136] Fix #31191 --- src/vs/workbench/parts/views/browser/treeView.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/parts/views/browser/treeView.ts b/src/vs/workbench/parts/views/browser/treeView.ts index c29199c28a2..401dff634ed 100644 --- a/src/vs/workbench/parts/views/browser/treeView.ts +++ b/src/vs/workbench/parts/views/browser/treeView.ts @@ -183,8 +183,8 @@ export class TreeView extends CollapsibleView { elements = elements ? elements : [this.tree.getInput()]; for (const element of elements) { element.children = null; + this.tree.refresh(element); } - this.tree.refresh(elements); } dispose(): void { From 622f2b989ab846b30d29254f4e554f98b782b782 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Tue, 25 Jul 2017 22:40:20 +0530 Subject: [PATCH 060/136] #29462: Improve message --- .../workbench/parts/preferences/browser/preferencesRenderers.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/parts/preferences/browser/preferencesRenderers.ts b/src/vs/workbench/parts/preferences/browser/preferencesRenderers.ts index a274ede5bf9..0f3cbc08421 100644 --- a/src/vs/workbench/parts/preferences/browser/preferencesRenderers.ts +++ b/src/vs/workbench/parts/preferences/browser/preferencesRenderers.ts @@ -1007,7 +1007,7 @@ class UnsupportedWorkbenchSettingsRenderer extends Disposable { private static _INVALID_SETTING_ = ModelDecorationOptions.register({ stickiness: editorCommon.TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges, inlineClassName: 'invalidSetting', - hoverMessage: nls.localize('unsupportedWorkbenchSetting', "This setting is a workbench setting and cannot be applied for resources under folder") + hoverMessage: nls.localize('unsupportedWorkbenchSetting', "Setting is dimmed because it cannot be configured for folder resources.") }); private createDecoration(range: IRange, model: editorCommon.IModel): editorCommon.IModelDeltaDecoration { From 597caf0595be7edd52d17e398c3757b61eb98bdc Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Tue, 25 Jul 2017 23:04:04 +0530 Subject: [PATCH 061/136] Fix #31084 --- .../services/configuration/common/configurationModels.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/services/configuration/common/configurationModels.ts b/src/vs/workbench/services/configuration/common/configurationModels.ts index 8acb1f796cb..824d742ff76 100644 --- a/src/vs/workbench/services/configuration/common/configurationModels.ts +++ b/src/vs/workbench/services/configuration/common/configurationModels.ts @@ -55,7 +55,7 @@ export class WorkspaceConfigurationModel extends CustomConfigurationModel private parseFolders(): URI[] { const folders: string[] = this._raw['folders'] || []; return distinct(folders.map(folder => URI.parse(folder)) - .filter(r => r.scheme === Schemas.file)); // only support files for now ; + .filter(r => r.scheme === Schemas.file), folder => folder.toString(true)); // only support files for now } private parseConfigurationModel(section: string): ConfigurationModel { From bde92c49a3e9c2d7343a931058f7ef1b0b4025de Mon Sep 17 00:00:00 2001 From: Ramya Achutha Rao Date: Tue, 25 Jul 2017 10:53:44 -0700 Subject: [PATCH 062/136] Map reflect css value command to the one in emmet extension --- extensions/emmet/src/extension.ts | 2 +- src/vs/workbench/parts/emmet/electron-browser/emmetActions.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/extensions/emmet/src/extension.ts b/extensions/emmet/src/extension.ts index 62b096ba1b0..9e95ad7e4e0 100644 --- a/extensions/emmet/src/extension.ts +++ b/extensions/emmet/src/extension.ts @@ -120,7 +120,7 @@ export function activate(context: vscode.ExtensionContext) { return updateImageSize(); })); - context.subscriptions.push(vscode.commands.registerCommand('emmet.reflectCssValue', () => { + context.subscriptions.push(vscode.commands.registerCommand('emmet.reflectCSSValue', () => { return reflectCssValue(); })); diff --git a/src/vs/workbench/parts/emmet/electron-browser/emmetActions.ts b/src/vs/workbench/parts/emmet/electron-browser/emmetActions.ts index 12ca2b1098c..be317a11000 100644 --- a/src/vs/workbench/parts/emmet/electron-browser/emmetActions.ts +++ b/src/vs/workbench/parts/emmet/electron-browser/emmetActions.ts @@ -259,7 +259,7 @@ export abstract class EmmetEditorAction extends EditorAction { 'editor.emmet.action.decrementNumberByOne': 'emmet.decrementNumberByOne', 'editor.emmet.action.decrementNumberByTen': 'emmet.decrementNumberByTen', 'editor.emmet.action.updateImageSize': 'emmet.updateImageSize', - 'editor.emmet.action.reflectCssValue': 'emmet.reflectCssValue' + 'editor.emmet.action.reflectCSSValue': 'emmet.reflectCSSValue' }; protected emmetActionName: string; From 3e45d86612e2eb695f234c9856d801d8b7552119 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Wed, 26 Jul 2017 00:11:00 +0530 Subject: [PATCH 063/136] Fix #30248 --- src/vs/workbench/parts/views/browser/views.ts | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/parts/views/browser/views.ts b/src/vs/workbench/parts/views/browser/views.ts index 2986eb9b7a4..c06fadfb51a 100644 --- a/src/vs/workbench/parts/views/browser/views.ts +++ b/src/vs/workbench/parts/views/browser/views.ts @@ -654,7 +654,13 @@ export class ComposedViewsViewlet extends Viewlet { } if (ViewLocation.getContributedViewLocation(this.location.id) && !this.areExtensionsReady) { // Checks in cache so that view do not jump. See #29609 - return this.viewsStates.size === 1; + let visibleViewsCount = 0; + this.viewsStates.forEach(viewState => { + if (!viewState.isHidden) { + visibleViewsCount++; + } + }); + return visibleViewsCount === 1; } return true; } From c1a169b9cdef6411c29e062b46feabbb2e8d0b1c Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Tue, 25 Jul 2017 11:41:06 -0700 Subject: [PATCH 064/136] Fix missing right border in search viewlet include/exclude boxes, and include/exclude misalignment with search text field --- src/vs/workbench/parts/search/browser/patternInputWidget.ts | 2 +- src/vs/workbench/parts/search/browser/searchViewlet.ts | 2 +- src/vs/workbench/parts/search/browser/searchWidget.ts | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/parts/search/browser/patternInputWidget.ts b/src/vs/workbench/parts/search/browser/patternInputWidget.ts index 5cbdbbb4135..204e87e13c0 100644 --- a/src/vs/workbench/parts/search/browser/patternInputWidget.ts +++ b/src/vs/workbench/parts/search/browser/patternInputWidget.ts @@ -109,7 +109,7 @@ export class PatternInputWidget extends Widget { } private setInputWidth(): void { - this.inputBox.width = this.width; + this.inputBox.width = this.width - this.getSubcontrolsWidth() - 2; // 2 for input box border } protected getSubcontrolsWidth(): number { diff --git a/src/vs/workbench/parts/search/browser/searchViewlet.ts b/src/vs/workbench/parts/search/browser/searchViewlet.ts index e8806b4501c..2cf35612ecf 100644 --- a/src/vs/workbench/parts/search/browser/searchViewlet.ts +++ b/src/vs/workbench/parts/search/browser/searchViewlet.ts @@ -694,7 +694,7 @@ export class SearchViewlet extends Viewlet { return; } - this.searchWidget.setWidth(this.size.width - 25 /* container margin */); + this.searchWidget.setWidth(this.size.width - 28 /* container margin */); this.inputPatternExcludes.setWidth(this.size.width - 28 /* container margin */); this.inputPatternIncludes.setWidth(this.size.width - 28 /* container margin */); diff --git a/src/vs/workbench/parts/search/browser/searchWidget.ts b/src/vs/workbench/parts/search/browser/searchWidget.ts index c9853dd70a1..54830f87372 100644 --- a/src/vs/workbench/parts/search/browser/searchWidget.ts +++ b/src/vs/workbench/parts/search/browser/searchWidget.ts @@ -146,7 +146,7 @@ export class SearchWidget extends Widget { } public setWidth(width: number) { - this.searchInput.setWidth(width - 2); + this.searchInput.setWidth(width); this.replaceInput.width = width - 28; } From 3ad42a169bd584d1a90417d1291d6971546f123f Mon Sep 17 00:00:00 2001 From: cleidigh Date: Tue, 25 Jul 2017 14:53:07 -0400 Subject: [PATCH 065/136] Extension download count - use current locale for number formatting --- src/vs/workbench/parts/extensions/browser/extensionsWidgets.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/parts/extensions/browser/extensionsWidgets.ts b/src/vs/workbench/parts/extensions/browser/extensionsWidgets.ts index 761519044e4..37627f3d030 100644 --- a/src/vs/workbench/parts/extensions/browser/extensionsWidgets.ts +++ b/src/vs/workbench/parts/extensions/browser/extensionsWidgets.ts @@ -9,6 +9,7 @@ import 'vs/css!./media/extensionsWidgets'; import { IDisposable, dispose } from 'vs/base/common/lifecycle'; import { IExtension, IExtensionsWorkbenchService } from '../common/extensions'; import { append, $, addClass } from 'vs/base/browser/dom'; +import * as platform from 'vs/base/common/platform'; export interface IOptions { extension?: IExtension; @@ -81,7 +82,7 @@ export class InstallWidget implements IDisposable { } } else { - installLabel = installCount.toLocaleString('en'); + installLabel = installCount.toLocaleString(platform.locale); } append(this.container, $('span.octicon.octicon-cloud-download')); From b208494b4a2b1e8a47b2903aab81c18be89adf82 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Wed, 26 Jul 2017 00:32:08 +0530 Subject: [PATCH 066/136] Fix #31409 --- .../preferences/browser/media/preferences.css | 2 +- .../browser/preferencesRenderers.ts | 64 ++++++++++++++++++- 2 files changed, 63 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/parts/preferences/browser/media/preferences.css b/src/vs/workbench/parts/preferences/browser/media/preferences.css index fb63ef3b79d..4351939a128 100644 --- a/src/vs/workbench/parts/preferences/browser/media/preferences.css +++ b/src/vs/workbench/parts/preferences/browser/media/preferences.css @@ -211,7 +211,7 @@ background: url('edit_inverse.svg') center center no-repeat; } -.monaco-editor .invalidSetting { +.monaco-editor .dim-configuration { color: #b1b1b1; } diff --git a/src/vs/workbench/parts/preferences/browser/preferencesRenderers.ts b/src/vs/workbench/parts/preferences/browser/preferencesRenderers.ts index 0f3cbc08421..2f4243b504b 100644 --- a/src/vs/workbench/parts/preferences/browser/preferencesRenderers.ts +++ b/src/vs/workbench/parts/preferences/browser/preferencesRenderers.ts @@ -31,6 +31,7 @@ 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"; export interface IPreferencesRenderer extends IDisposable { preferencesModel: IPreferencesEditorModel; @@ -171,6 +172,7 @@ export class UserSettingsRenderer extends Disposable implements IPreferencesRend export class WorkspaceSettingsRenderer extends UserSettingsRenderer implements IPreferencesRenderer { private untrustedSettingRenderer: UnsupportedWorkspaceSettingsRenderer; + private workspaceConfigurationRenderer: WorkspaceConfigurationRenderer; constructor(editor: ICodeEditor, preferencesModel: SettingsEditorModel, associatedPreferencesModel: IPreferencesEditorModel, @IPreferencesService preferencesService: IPreferencesService, @@ -182,6 +184,7 @@ export class WorkspaceSettingsRenderer extends UserSettingsRenderer implements I ) { super(editor, preferencesModel, associatedPreferencesModel, preferencesService, telemetryService, textFileService, configurationEditingService, messageService, instantiationService); this.untrustedSettingRenderer = this._register(instantiationService.createInstance(UnsupportedWorkspaceSettingsRenderer, editor, preferencesModel)); + this.workspaceConfigurationRenderer = this._register(instantiationService.createInstance(WorkspaceConfigurationRenderer, editor, preferencesModel)); } protected createHeader(): void { @@ -191,6 +194,7 @@ export class WorkspaceSettingsRenderer extends UserSettingsRenderer implements I public render(): void { super.render(); this.untrustedSettingRenderer.render(); + this.workspaceConfigurationRenderer.render(); } } @@ -975,7 +979,6 @@ class UnsupportedWorkbenchSettingsRenderer extends Disposable { constructor(private editor: editorCommon.ICommonCodeEditor, private workspaceSettingsEditorModel: SettingsEditorModel, @IWorkspaceConfigurationService private configurationService: IWorkspaceConfigurationService, - @IMarkerService private markerService: IMarkerService ) { super(); this._register(this.configurationService.onDidUpdateConfiguration(() => this.render())); @@ -1006,7 +1009,7 @@ class UnsupportedWorkbenchSettingsRenderer extends Disposable { private static _INVALID_SETTING_ = ModelDecorationOptions.register({ stickiness: editorCommon.TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges, - inlineClassName: 'invalidSetting', + inlineClassName: 'dim-configuration', hoverMessage: nls.localize('unsupportedWorkbenchSetting', "Setting is dimmed because it cannot be configured for folder resources.") }); @@ -1017,6 +1020,63 @@ class UnsupportedWorkbenchSettingsRenderer extends Disposable { }; } + public dispose(): void { + if (this.decorationIds) { + this.decorationIds = this.editor.changeDecorations(changeAccessor => { + return changeAccessor.deltaDecorations(this.decorationIds, []); + }); + } + super.dispose(); + } +} + +class WorkspaceConfigurationRenderer extends Disposable { + + private decorationIds: string[] = []; + private renderingDelayer: Delayer = new Delayer(200); + + constructor(private editor: editorCommon.ICommonCodeEditor, private workspaceSettingsEditorModel: SettingsEditorModel, + @IWorkspaceContextService private workspaceContextService: IWorkspaceContextService + ) { + super(); + this._register(this.editor.getModel().onDidChangeContent(() => this.renderingDelayer.trigger(() => this.render()))); + } + + public render(): void { + if (this.workspaceContextService.hasMultiFolderWorkspace()) { + this.editor.changeDecorations(changeAccessor => this.decorationIds = changeAccessor.deltaDecorations(this.decorationIds, [])); + + const ranges: IRange[] = []; + for (const settingsGroup of this.workspaceSettingsEditorModel.settingsGroups) { + for (const section of settingsGroup.sections) { + for (const setting of section.settings) { + if (setting.key !== 'settings') { + ranges.push({ + startLineNumber: setting.keyRange.startLineNumber, + startColumn: setting.keyRange.startColumn - 1, + endLineNumber: setting.valueRange.endLineNumber, + endColumn: setting.valueRange.endColumn + }); + } + } + } + } + this.editor.changeDecorations(changeAccessor => this.decorationIds = changeAccessor.deltaDecorations(this.decorationIds, ranges.map(range => this.createDecoration(range, this.editor.getModel())))); + } + } + + private static _DIM_CONFIGURATION_ = ModelDecorationOptions.register({ + stickiness: editorCommon.TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges, + inlineClassName: 'dim-configuration' + }); + + private createDecoration(range: IRange, model: editorCommon.IModel): editorCommon.IModelDeltaDecoration { + return { + range, + options: WorkspaceConfigurationRenderer._DIM_CONFIGURATION_ + }; + } + public dispose(): void { if (this.decorationIds) { this.decorationIds = this.editor.changeDecorations(changeAccessor => { From 0bf86665d2b8076196f4e9044d6cc9e23afba49e Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Wed, 26 Jul 2017 00:43:09 +0530 Subject: [PATCH 067/136] #29462 Improve wording --- .../workbench/parts/preferences/browser/preferencesRenderers.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/parts/preferences/browser/preferencesRenderers.ts b/src/vs/workbench/parts/preferences/browser/preferencesRenderers.ts index 2f4243b504b..f733eda260c 100644 --- a/src/vs/workbench/parts/preferences/browser/preferencesRenderers.ts +++ b/src/vs/workbench/parts/preferences/browser/preferencesRenderers.ts @@ -1010,7 +1010,7 @@ class UnsupportedWorkbenchSettingsRenderer extends Disposable { private static _INVALID_SETTING_ = ModelDecorationOptions.register({ stickiness: editorCommon.TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges, inlineClassName: 'dim-configuration', - hoverMessage: nls.localize('unsupportedWorkbenchSetting', "Setting is dimmed because it cannot be configured for folder resources.") + hoverMessage: nls.localize('unsupportedWorkbenchSetting', "This setting cannot be configured for folder resources.") }); private createDecoration(range: IRange, model: editorCommon.IModel): editorCommon.IModelDeltaDecoration { From ac9ceddde48e5fe666a40dd3e3b650e5789df056 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Wed, 26 Jul 2017 00:55:23 +0530 Subject: [PATCH 068/136] Fix #30333 --- src/vs/workbench/parts/views/browser/treeView.ts | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/parts/views/browser/treeView.ts b/src/vs/workbench/parts/views/browser/treeView.ts index 401dff634ed..d0a27da1dbb 100644 --- a/src/vs/workbench/parts/views/browser/treeView.ts +++ b/src/vs/workbench/parts/views/browser/treeView.ts @@ -31,6 +31,7 @@ import { IExtensionService } from 'vs/platform/extensions/common/extensions'; import { CollapsibleState, ViewSizing } from 'vs/base/browser/ui/splitview/splitview'; import { CollapsibleView, IViewletViewOptions, IViewOptions } from 'vs/workbench/parts/views/browser/views'; import { ICommandService } from 'vs/platform/commands/common/commands'; +import { IWorkbenchThemeService } from "vs/workbench/services/themes/common/workbenchThemeService"; export class TreeView extends CollapsibleView { @@ -51,6 +52,7 @@ export class TreeView extends CollapsibleView { @IInstantiationService private instantiationService: IInstantiationService, @IListService private listService: IListService, @IThemeService private themeService: IThemeService, + @IWorkbenchThemeService private workbenchThemeService: IWorkbenchThemeService, @IContextKeyService private contextKeyService: IContextKeyService, @IExtensionService private extensionService: IExtensionService, @ICommandService private commandService: ICommandService @@ -60,6 +62,7 @@ export class TreeView extends CollapsibleView { this.viewFocusContext = this.contextKeyService.createKey(this.id, void 0); this.menus.onDidChangeTitle(() => this.updateActions(), this, this.disposables); this.themeService.onThemeChange(() => this.tree.refresh() /* soft refresh */, this, this.disposables); + this.workbenchThemeService.onDidFileIconThemeChange(() => this.tree.refresh() /* soft refresh */, this, this.disposables); if (!options.collapsed) { this.activate(); } @@ -262,7 +265,8 @@ class TreeRenderer implements IRenderer { private static ITEM_HEIGHT = 22; private static TREE_TEMPLATE_ID = 'treeExplorer'; - constructor( @IThemeService private themeService: IThemeService) { + constructor( @IThemeService private themeService: IThemeService, + @IWorkbenchThemeService private workbenchThemeService: IWorkbenchThemeService) { } public getHeight(tree: ITree, element: any): number { @@ -289,7 +293,8 @@ class TreeRenderer implements IRenderer { templateData.label.text(node.label).title(node.label); const theme = this.themeService.getTheme(); - const icon = theme.type === LIGHT ? node.icon : node.iconDark; + const fileIconTheme = this.workbenchThemeService.getFileIconTheme(); + const icon = (fileIconTheme.hasFileIcons || fileIconTheme.hasFolderIcons) ? (theme.type === LIGHT ? node.icon : node.iconDark) : null; if (icon) { templateData.icon.getHTMLElement().style.backgroundImage = `url('${icon}')`; From 56cfb2754a37f532e17a40c01fc3490b9d63c38f Mon Sep 17 00:00:00 2001 From: Andre Weinand Date: Tue, 25 Jul 2017 21:49:46 +0200 Subject: [PATCH 069/136] promote 'startDebugging' and remove 'startDebugSession' from debug API --- src/vs/vscode.d.ts | 17 +++++++++-------- src/vs/vscode.proposed.d.ts | 15 --------------- src/vs/workbench/api/node/extHost.api.impl.ts | 5 +---- src/vs/workbench/api/node/extHostApiCommands.ts | 4 ++-- .../debug/electron-browser/debugCommands.ts | 15 ++++++++++++--- 5 files changed, 24 insertions(+), 32 deletions(-) diff --git a/src/vs/vscode.d.ts b/src/vs/vscode.d.ts index 16f71db579a..1900cd31d29 100644 --- a/src/vs/vscode.d.ts +++ b/src/vs/vscode.d.ts @@ -5522,15 +5522,16 @@ declare module 'vscode' { export namespace debug { /** - * Start a debug session based on the given configuration. - * The configuration's type is used to select the debug adapter and then the configuration is directly passed to this adapter. - * This function should only be called in the context of a 'startSession' command, e.g. after verifying or massaging the configuration. - * Folder specific variables used in the configuration (e.g. '${workspaceRoot}') are resolved against the given folder. - * @param folder The workspace folder for resolving variables used in the configuration or undefined. - * @param configuration The debug configuration that is directly passed to the debug adapter. - * @return A thenable that resolves when the debug session could be successfully started. + * Start debugging by using either a named launch or named compound configuration, + * or by directly passing a DebugConfiguration. + * The named configurations are looked up in '.vscode/launch.json' found in the given folder. + * Before debugging starts, all unsaved files are saved and the launch configurations are brought up-to-date. + * Folder specific variables used in the configuration (e.g. 'workspaceRoot') are resolved against the given folder. + * @param folder The workspace folder for looking up named configurations and resolving variables or undefined. + * @param nameOrConfiguration Either the name of a debug or compound configuration or a DebugConfiguration object. + * @return A thenable that resolves when debugging could be successfully started. */ - export function startDebugSession(folder: WorkspaceFolder | undefined, configuration: DebugConfiguration): Thenable; + export function startDebugging(folder: WorkspaceFolder | undefined, nameOrConfiguration: string | DebugConfiguration): Thenable; /** * The currently active debug session or `undefined`. The active debug session is the one diff --git a/src/vs/vscode.proposed.d.ts b/src/vs/vscode.proposed.d.ts index 8dd39d6f190..cf8170b7be7 100644 --- a/src/vs/vscode.proposed.d.ts +++ b/src/vs/vscode.proposed.d.ts @@ -55,21 +55,6 @@ declare module 'vscode' { export function registerDiffInformationCommand(command: string, callback: (diff: LineChange[], ...args: any[]) => any, thisArg?: any): Disposable; } - export namespace debug { - - /** - * Start debugging by using either a named launch or named compound configuration, - * or by directly passing a DebugConfiguration. - * The named configurations are looked up in '.vscode/launch.json' found in the given folder. - * Before debugging starts, all unsaved files are saved and the launch configurations are brought up-to-date. - * Folder specific variables used in the configuration (e.g. 'workspaceRoot') are resolved against the given folder. - * @param folder The workspace folder for looking up named configurations and resolving variables or undefined. - * @param nameOrConfiguration Either the name of a debug or compound configuration or a DebugConfiguration object. - * @return A thenable that resolves when debugging could be successfully started. - */ - export function startDebugging(folder: WorkspaceFolder | undefined, nameOrConfiguration: string | DebugConfiguration): Thenable; - } - /** * Namespace for handling credentials. */ diff --git a/src/vs/workbench/api/node/extHost.api.impl.ts b/src/vs/workbench/api/node/extHost.api.impl.ts index d67938243a1..2bcb314f32d 100644 --- a/src/vs/workbench/api/node/extHost.api.impl.ts +++ b/src/vs/workbench/api/node/extHost.api.impl.ts @@ -477,11 +477,8 @@ export function createApiFactory( get activeDebugSession() { return extHostDebugService.activeDebugSession; }, - startDebugging: proposedApiFunction(extension, (folder: vscode.WorkspaceFolder | undefined, nameOrConfig: string | vscode.DebugConfiguration) => { + startDebugging(folder: vscode.WorkspaceFolder | undefined, nameOrConfig: string | vscode.DebugConfiguration) { return extHostDebugService.startDebugging(folder, nameOrConfig); - }), - startDebugSession(folder: vscode.WorkspaceFolder | undefined, config: vscode.DebugConfiguration) { - return extHostDebugService.startDebugSession(folder, config); }, onDidStartDebugSession(listener, thisArg?, disposables?) { return extHostDebugService.onDidStartDebugSession(listener, thisArg, disposables); diff --git a/src/vs/workbench/api/node/extHostApiCommands.ts b/src/vs/workbench/api/node/extHostApiCommands.ts index 1e7c3c49161..aeb9c080997 100644 --- a/src/vs/workbench/api/node/extHostApiCommands.ts +++ b/src/vs/workbench/api/node/extHostApiCommands.ts @@ -195,8 +195,8 @@ export class ExtHostApiCommands { ] }); - this._register('vscode.startDebug', (configuration?: any) => { - return this._commands.executeCommand('_workbench.startDebug', configuration); + this._register('vscode.startDebug', (configuration?: any, folderUri?: URI) => { + return this._commands.executeCommand('_workbench.startDebug', configuration, folderUri); }, { description: 'Start a debugging session.', args: [ diff --git a/src/vs/workbench/parts/debug/electron-browser/debugCommands.ts b/src/vs/workbench/parts/debug/electron-browser/debugCommands.ts index 49d609d43a4..8506344deee 100644 --- a/src/vs/workbench/parts/debug/electron-browser/debugCommands.ts +++ b/src/vs/workbench/parts/debug/electron-browser/debugCommands.ts @@ -4,6 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import * as nls from 'vs/nls'; +import uri from 'vs/base/common/uri'; import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; import { TPromise } from 'vs/base/common/winjs.base'; import severity from 'vs/base/common/severity'; @@ -25,16 +26,24 @@ export function registerCommands(): void { KeybindingsRegistry.registerCommandAndKeybindingRule({ id: '_workbench.startDebug', weight: KeybindingsRegistry.WEIGHT.workbenchContrib(), - handler(accessor: ServicesAccessor, configurationOrName: IConfig | string) { + handler(accessor: ServicesAccessor, configurationOrName: IConfig | string, folderUri?: uri) { const debugService = accessor.get(IDebugService); if (!configurationOrName) { configurationOrName = debugService.getConfigurationManager().selectedName; } + if (!folderUri) { + const contextService = accessor.get(IWorkspaceContextService); + const workspace = contextService.getWorkspace(); + if (workspace && workspace.roots.length > 0) { + folderUri = workspace.roots[0]; + } + } + if (typeof configurationOrName === 'string') { - debugService.startDebugging(undefined, configurationOrName); + debugService.startDebugging(folderUri, configurationOrName); } else { - debugService.createProcess(undefined, configurationOrName); + debugService.createProcess(folderUri, configurationOrName); } }, when: CONTEXT_NOT_IN_DEBUG_MODE, From 4a4226e45385e06386c583c861f316405bbcc20b Mon Sep 17 00:00:00 2001 From: Ramya Achutha Rao Date: Tue, 25 Jul 2017 13:00:58 -0700 Subject: [PATCH 070/136] Refining position validations for emmet completions in scss #30188 --- extensions/emmet/src/abbreviationActions.ts | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/extensions/emmet/src/abbreviationActions.ts b/extensions/emmet/src/abbreviationActions.ts index f5cea763b10..d9a5463f34b 100644 --- a/extensions/emmet/src/abbreviationActions.ts +++ b/extensions/emmet/src/abbreviationActions.ts @@ -156,7 +156,7 @@ export function expandAbbreviation(args) { */ export function isValidLocationForEmmetAbbreviation(currentNode: Node, syntax: string, position: vscode.Position): boolean { if (!currentNode) { - return true; + return !isStyleSheet(syntax); } if (isStyleSheet(syntax)) { @@ -164,6 +164,16 @@ export function isValidLocationForEmmetAbbreviation(currentNode: Node, syntax: s return true; } const currentCssNode = currentNode; + + // Workaround for https://github.com/Microsoft/vscode/30188 + if (currentCssNode.parent + && currentCssNode.parent.type === 'rule' + && currentCssNode.selectorToken + && currentCssNode.selectorToken.start.line !== currentCssNode.selectorToken.end.line) { + return true; + } + + // Position is valid if it occurs after the `{` that marks beginning of rule contents return currentCssNode.selectorToken && position.isAfter(currentCssNode.selectorToken.end); } From 048012e4d279f87c15675b459eb2bd83a0d03724 Mon Sep 17 00:00:00 2001 From: Andre Weinand Date: Tue, 25 Jul 2017 22:17:09 +0200 Subject: [PATCH 071/136] update to DAP 1.22.0 --- npm-shrinkwrap.json | 6 ++--- package.json | 2 +- .../parts/debug/common/debugProtocol.d.ts | 23 ++++++++++++++++++- 3 files changed, 26 insertions(+), 5 deletions(-) diff --git a/npm-shrinkwrap.json b/npm-shrinkwrap.json index e35567fe089..06c57d32499 100644 --- a/npm-shrinkwrap.json +++ b/npm-shrinkwrap.json @@ -422,9 +422,9 @@ "resolved": "git://github.com/jrieken/v8-profiler.git#5e4a336693e1d5b079c7aecd286a1abcfbc10421" }, "vscode-debugprotocol": { - "version": "1.21.0", - "from": "vscode-debugprotocol@1.21.0", - "resolved": "https://registry.npmjs.org/vscode-debugprotocol/-/vscode-debugprotocol-1.21.0.tgz" + "version": "1.22.0", + "from": "vscode-debugprotocol@1.22.0", + "resolved": "https://registry.npmjs.org/vscode-debugprotocol/-/vscode-debugprotocol-1.22.0.tgz" }, "vscode-ripgrep": { "version": "0.0.12", diff --git a/package.json b/package.json index 71a03a6eeb6..6dbb4c4d4bf 100644 --- a/package.json +++ b/package.json @@ -39,7 +39,7 @@ "nsfw": "1.0.16", "semver": "4.3.6", "v8-profiler": "jrieken/v8-profiler#vscode", - "vscode-debugprotocol": "1.21.0", + "vscode-debugprotocol": "1.22.0", "vscode-ripgrep": "joaomoreno/vscode-ripgrep#64a2f0530fd9099de2cb1026a61e0a31b05e983d", "vscode-textmate": "^3.1.5", "winreg": "1.2.0", diff --git a/src/vs/workbench/parts/debug/common/debugProtocol.d.ts b/src/vs/workbench/parts/debug/common/debugProtocol.d.ts index 04ae77ec97e..9f455747f08 100644 --- a/src/vs/workbench/parts/debug/common/debugProtocol.d.ts +++ b/src/vs/workbench/parts/debug/common/debugProtocol.d.ts @@ -184,6 +184,27 @@ declare module DebugProtocol { }; } + /** Event message for 'process' event type. + The event indicates that the debugger has begun debugging a new process. Either one that it has launched, or one that it has attached to. + */ + export interface ProcessEvent extends Event { + // event: 'process'; + body: { + /** The logical name of the process. This is usually the full path to process's executable file. Example: /home/example/myproj/program.js. */ + name: string; + /** The system process id of the debugged process. This property will be missing for non-system processes. */ + systemProcessId?: number; + /** If true, the process is running on the same computer as the debug adapter. */ + isLocalProcess?: boolean; + /** Describes how the debug engine started debugging this process. + launch: Process was launched under the debugger. + attach: Debugger attached to an existing process. + attachForSuspendedLaunch: A project launcher component has launched a new process in a suspended state and then asked the debugger to attach. + */ + startMethod?: 'launch' | 'attach' | 'attachForSuspendedLaunch'; + }; + } + /** runInTerminal request; value of command field is 'runInTerminal'. With this request a debug adapter can run a command in a terminal. */ @@ -1098,7 +1119,7 @@ declare module DebugProtocol { /** If sourceReference > 0 the contents of the source must be retrieved through the SourceRequest (even if a path is specified). A sourceReference is only valid for a session, so it must not be used to persist a source. */ sourceReference?: number; /** An optional hint for how to present the source in the UI. A value of 'deemphasize' can be used to indicate that the source is not available or that it is skipped on stepping. */ - presentationHint?: 'emphasize' | 'deemphasize'; + presentationHint?: 'normal' | 'emphasize' | 'deemphasize'; /** The (optional) origin of this source: possible values 'internal module', 'inlined content from source map', etc. */ origin?: string; /** Optional data that a debug adapter might want to loop through the client. The client should leave the data intact and persist it across sessions. The client should not interpret the data. */ From 53a9c0276dc2565904b1bfbdcd473dd860497846 Mon Sep 17 00:00:00 2001 From: Andre Weinand Date: Tue, 25 Jul 2017 22:26:42 +0200 Subject: [PATCH 072/136] node-debug@1.15.15 --- build/gulpfile.vscode.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/gulpfile.vscode.js b/build/gulpfile.vscode.js index 61b16090fa2..198e50387c4 100644 --- a/build/gulpfile.vscode.js +++ b/build/gulpfile.vscode.js @@ -43,7 +43,7 @@ const nodeModules = ['electron', 'original-fs'] // Build const builtInExtensions = [ - { name: 'ms-vscode.node-debug', version: '1.15.14' }, + { name: 'ms-vscode.node-debug', version: '1.15.15' }, { name: 'ms-vscode.node-debug2', version: '1.14.5' } ]; From 2c478ea5c21998de53148d33be16fa9ed3e0920b Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Wed, 26 Jul 2017 01:59:33 +0530 Subject: [PATCH 073/136] Fix compilation errors --- .../search/browser/patternInputWidget.ts | 36 ------------------- .../parts/search/browser/searchViewlet.ts | 10 +++--- 2 files changed, 4 insertions(+), 42 deletions(-) diff --git a/src/vs/workbench/parts/search/browser/patternInputWidget.ts b/src/vs/workbench/parts/search/browser/patternInputWidget.ts index a9fcd70533d..62be8e7e02b 100644 --- a/src/vs/workbench/parts/search/browser/patternInputWidget.ts +++ b/src/vs/workbench/parts/search/browser/patternInputWidget.ts @@ -165,42 +165,6 @@ export class PatternInputWidget extends Widget { this.inputFocusTracker = dom.trackFocus(this.inputBox.inputElement); this.onkeyup(this.inputBox.inputElement, (keyboardEvent) => this.onInputKeyUp(keyboardEvent)); - this.pattern = new Checkbox({ - actionClassName: 'pattern', - title: nls.localize('patternDescription', "Use Glob Patterns"), - isChecked: false, - onChange: (viaKeyboard) => { - this.onOptionChange(null); - if (!viaKeyboard) { - this.inputBox.focus(); - } - - if (this.isGlobPattern()) { - this.showGlobHelp(); - } else { - this.inputBox.hideMessage(); - } - } - }); - this._register(attachCheckboxStyler(this.pattern, this.themeService)); - - this._register(this.onSubmit(() => { - let value = this.getValue(); - if (value.length > 0) { - this.history.add(this.getValue()); - } - })); - - $(this.pattern.domNode).on('mouseover', () => { - if (this.isGlobPattern()) { - this.showGlobHelp(); - } - }); - - $(this.pattern.domNode).on(['mouseleave', 'mouseout'], () => { - this.inputBox.hideMessage(); - }); - let controls = document.createElement('div'); controls.className = 'controls'; this.renderSubcontrols(controls); diff --git a/src/vs/workbench/parts/search/browser/searchViewlet.ts b/src/vs/workbench/parts/search/browser/searchViewlet.ts index 851798e31ce..6a907423c1b 100644 --- a/src/vs/workbench/parts/search/browser/searchViewlet.ts +++ b/src/vs/workbench/parts/search/browser/searchViewlet.ts @@ -169,8 +169,6 @@ export class SearchViewlet extends Viewlet { const filePatterns = this.viewletSettings['query.filePatterns'] || ''; const patternExclusions = this.viewletSettings['query.folderExclusions'] || ''; const patternExclusionsHistory = this.viewletSettings['query.folderExclusionsHistory'] || []; - const exclusionsUsePattern = this.viewletSettings['query.exclusionsUsePattern']; - const includesUsePattern = this.viewletSettings['query.includesUsePattern']; const patternIncludes = this.viewletSettings['query.folderIncludes'] || ''; const patternIncludesHistory = this.viewletSettings['query.folderIncludesHistory'] || []; const queryDetailsExpanded = this.viewletSettings['query.queryDetailsExpanded'] || ''; @@ -223,17 +221,17 @@ export class SearchViewlet extends Viewlet { ariaLabel: nls.localize('label.excludes', 'Search Exclude Patterns') }); - this.inputPatternExcludes.setValue(patternExclusions); + this.inputPatternExcludes.setValue(patternExclusions); this.inputPatternExcludes.setUseIgnoreFiles(useIgnoreFiles); this.inputPatternExcludes.setUseExcludeSettings(useExcludeSettings); this.inputPatternExcludes.setHistory(patternExclusionsHistory); - + this.inputPatternExcludes .on(FindInput.OPTION_CHANGE, (e) => { this.onQueryChanged(false); }); - this.inputPatternExcludes.onSubmit(() => this.onQueryChanged(true, true)); + this.inputPatternExcludes.onSubmit(() => this.onQueryChanged(true, true)); this.trackInputBox(this.inputPatternExcludes.inputFocusTracker, this.inputPatternExclusionsFocussed); }); }).getHTMLElement(); @@ -269,7 +267,7 @@ export class SearchViewlet extends Viewlet { } public get searchExcludePattern(): PatternInputWidget { - return this.inputPatternExclusions; + return this.inputPatternExcludes; } private createSearchWidget(builder: Builder): void { From fb200985123470c4354b4a7b3ac9ee471320dc13 Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Tue, 25 Jul 2017 10:59:54 -0700 Subject: [PATCH 074/136] Implement include/exclude precedence for non-rg search --- .../services/search/node/fileSearch.ts | 43 +++++++++++++------ 1 file changed, 30 insertions(+), 13 deletions(-) diff --git a/src/vs/workbench/services/search/node/fileSearch.ts b/src/vs/workbench/services/search/node/fileSearch.ts index f77c6390bec..2362e4c21d3 100644 --- a/src/vs/workbench/services/search/node/fileSearch.ts +++ b/src/vs/workbench/services/search/node/fileSearch.ts @@ -90,7 +90,7 @@ export class FileWalker { this.folderExcludePatterns = new Map(); config.folderQueries.forEach(folderQuery => { - const folderExcludeExpression: glob.IExpression = objects.assign({}, folderQuery.excludePattern || {}, this.config.excludePattern || {}); + const folderExcludeExpression = objects.clone(folderQuery.excludePattern || {}); // Add excludes for other root folders config.folderQueries @@ -144,7 +144,7 @@ export class FileWalker { } // File: Check for match on file pattern and include pattern - this.matchFile(onResult, { relativePath: extraFilePath /* no workspace relative path */, basename }); + this.matchFile(onResult, { relativePath: extraFilePath /* no workspace relative path */, basename }, []); }); } @@ -279,9 +279,10 @@ export class FileWalker { * Public for testing. */ public spawnFindCmd(folderQuery: IFolderSearch) { - const excludePattern = this.folderExcludePatterns.get(folderQuery.folder); - const basenames = glob.getBasenameTerms(excludePattern); - const pathTerms = glob.getPathTerms(excludePattern); + const folderExcludePattern = this.folderExcludePatterns.get(folderQuery.folder); + const excludes = [folderExcludePattern, this.globalExcludePattern].filter(excludeSet => !!excludeSet); + const basenames = arrays.flatten(excludes.map(glob.getBasenameTerms)); + const pathTerms = arrays.flatten(excludes.map(glob.getPathTerms)); let args = ['-L', '.']; if (basenames.length || pathTerms.length) { args.push('-not', '(', '('); @@ -379,7 +380,7 @@ export class FileWalker { // Support relative paths to files from a root resource (ignores excludes) if (relativeFiles.indexOf(this.filePattern) !== -1) { const basename = path.basename(this.filePattern); - this.matchFile(onResult, { base: base, relativePath: this.filePattern, basename }); + this.matchFile(onResult, { base: base, relativePath: this.filePattern, basename }, undefined, undefined, /*skipExcludes=*/true); } function add(relativePath: string) { @@ -401,7 +402,6 @@ export class FileWalker { private matchDirectoryTree({ rootEntries, pathToEntries }: IDirectoryTree, rootFolder: string, onResult: (result: IRawFileMatch) => void) { const self = this; - const excludePattern = this.folderExcludePatterns.get(rootFolder); const filePattern = this.filePattern; function matchDirectory(entries: IDirectoryEntry[]) { self.directoriesWalked++; @@ -413,7 +413,8 @@ export class FileWalker { // If the user searches for the exact file name, we adjust the glob matching // to ignore filtering by siblings because the user seems to know what she // is searching for and we want to include the result in that case anyway - if (excludePattern(relativePath, basename, () => filePattern !== basename ? entries.map(entry => entry.basename) : [])) { + const siblings = filePattern !== basename ? entries.map(entry => entry.basename) : []; + if (!self.includeMatch(entry, siblings, rootFolder, /*disregardInclude=*/true)) { continue; } @@ -426,7 +427,7 @@ export class FileWalker { continue; // ignore file if its path matches with the file pattern because that is already matched above } - self.matchFile(onResult, entry); + self.matchFile(onResult, entry, siblings, rootFolder); } }; } @@ -521,7 +522,7 @@ export class FileWalker { // Check exclude pattern let currentRelativePath = relativeParentPath ? [relativeParentPath, file].join(path.sep) : file; - if (this.folderExcludePatterns.get(folderQuery.folder)(currentRelativePath, file, () => siblings)) { + if (!this.includeMatch({ relativePath: currentRelativePath, basename: file }, siblings, folderQuery.folder, /*disregardInclude=*/true)) { return clb(null, undefined); } @@ -578,7 +579,7 @@ export class FileWalker { return clb(null, undefined); // ignore file if max file size is hit } - this.matchFile(onResult, { base: rootFolder, relativePath: currentRelativePath, basename: file, size: stat.size }); + this.matchFile(onResult, { base: rootFolder, relativePath: currentRelativePath, basename: file, size: stat.size }, siblings, folderQuery.folder); } // Unwind @@ -594,8 +595,8 @@ export class FileWalker { }); } - private matchFile(onResult: (result: IRawFileMatch) => void, candidate: IRawFileMatch): void { - if (this.isFilePatternMatch(candidate.relativePath) && (!this.includePattern || this.includePattern(candidate.relativePath, candidate.basename))) { + private matchFile(onResult: (result: IRawFileMatch) => void, candidate: IRawFileMatch, siblings: string[], folder?: string, skipIncludeExclude = false): void { + if (this.isFilePatternMatch(candidate.relativePath) && (skipIncludeExclude || this.includeMatch(candidate, siblings, folder))) { this.resultCount++; if (this.maxResults && this.resultCount > this.maxResults) { @@ -608,6 +609,22 @@ export class FileWalker { } } + private includeMatch(candidate: IRawFileMatch, siblings: string[], folder?: string, disregardInclude = false): boolean { + if (this.globalExcludePattern && this.globalExcludePattern(candidate.relativePath, candidate.basename, () => siblings)) { + return false; + } + + if (this.includePattern) { + return disregardInclude || !!this.includePattern(candidate.relativePath, candidate.basename); + } + + if (this.folderExcludePatterns.has(folder)) { + return !this.folderExcludePatterns.get(folder)(candidate.relativePath, candidate.basename, () => siblings); + } else { + return true; + } + } + private isFilePatternMatch(path: string): boolean { // Check for search pattern From 41f9e654b4003d97d9ac12b2599c5567d3569a00 Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Tue, 25 Jul 2017 14:07:27 -0700 Subject: [PATCH 075/136] Enable multiroot folder exclude precedence test --- .../test/node/textSearch.integrationTest.ts | 23 +++++++++---------- 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/src/vs/workbench/services/search/test/node/textSearch.integrationTest.ts b/src/vs/workbench/services/search/test/node/textSearch.integrationTest.ts index 61ae7e7cc71..00dc96da0c6 100644 --- a/src/vs/workbench/services/search/test/node/textSearch.integrationTest.ts +++ b/src/vs/workbench/services/search/test/node/textSearch.integrationTest.ts @@ -284,19 +284,18 @@ suite('Search-integration', function () { doSearchTest(config, 286, done); }); - // Pending non-ripgrep precedence - // test('Multiroot: e with folder exclude precedence', function (done: () => void) { - // const config: IRawSearch = { - // folderQueries: [ - // { folder: EXAMPLES_FIXTURES, excludePattern: makeExpression('**/e*.js') }, - // { folder: MORE_FIXTURES } - // ], - // contentPattern: { pattern: 'e' }, - // includePattern: makeExpression('**/*.js') - // }; + test('Multiroot: e with folder exclude precedence', function (done: () => void) { + const config: IRawSearch = { + folderQueries: [ + { folder: EXAMPLES_FIXTURES, excludePattern: makeExpression('**/e*.js') }, + { folder: MORE_FIXTURES } + ], + contentPattern: { pattern: 'e' }, + includePattern: makeExpression('**/*.js') + }; - // doSearchTest(config, 382, done); - // }); + doSearchTest(config, 382, done); + }); }); function makeExpression(...patterns: string[]): glob.IExpression { From 2ff7d989d01a01971340929a86fa59dd0d8ab340 Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Tue, 25 Jul 2017 15:41:44 -0700 Subject: [PATCH 076/136] Fix 'when' excludes broken on windows, add test. Fixes #31395 --- .../services/search/node/ripgrepTextSearch.ts | 16 ++++++++++------ .../search/test/node/ripgrepTextSearch.test.ts | 4 ++-- .../test/node/textSearch.integrationTest.ts | 18 ++++++++++++++---- 3 files changed, 26 insertions(+), 12 deletions(-) diff --git a/src/vs/workbench/services/search/node/ripgrepTextSearch.ts b/src/vs/workbench/services/search/node/ripgrepTextSearch.ts index 2ae6c703205..607ea849557 100644 --- a/src/vs/workbench/services/search/node/ripgrepTextSearch.ts +++ b/src/vs/workbench/services/search/node/ripgrepTextSearch.ts @@ -422,7 +422,7 @@ function globExprsToRgGlobs(patterns: glob.IExpression, folder: string): IRgGlob key = getAbsoluteGlob(folder, key); if (typeof value === 'boolean' && value) { - globArgs.push(key); + globArgs.push(fixDriveC(key)); } else if (value && value.when) { if (!siblingClauses) { siblingClauses = {}; @@ -442,14 +442,16 @@ function globExprsToRgGlobs(patterns: glob.IExpression, folder: string): IRgGlob * Exported for testing */ export function getAbsoluteGlob(folder: string, key: string): string { - const absolutePathKey = paths.isAbsolute(key) ? + return paths.isAbsolute(key) ? key : path.join(folder, key); +} - const root = paths.getRoot(folder); +export function fixDriveC(path: string): string { + const root = paths.getRoot(path); return root.toLowerCase() === 'c:/' ? - absolutePathKey.replace(/^c:[/\\]/i, '/') : - absolutePathKey; + path.replace(/^c:[/\\]/i, '/') : + path; } function getRgArgs(config: IRawSearch): IRgGlobResult { @@ -471,7 +473,9 @@ function getRgArgs(config: IRawSearch): IRgGlobResult { args.push(...globsToGlobArgs(globalExcludeResult.globArgs.map(globToNotGlob))); // includePattern can't have siblingClauses - const siblingClauses = excludeResult.siblingClauses; + const siblingClauses = (excludeResult.siblingClauses || globalExcludeResult.siblingClauses) ? + objects.assign({}, excludeResult.siblingClauses || {}, globalExcludeResult.siblingClauses || {}) : + null; if (config.maxFilesize) { args.push('--max-filesize', config.maxFilesize + ''); diff --git a/src/vs/workbench/services/search/test/node/ripgrepTextSearch.test.ts b/src/vs/workbench/services/search/test/node/ripgrepTextSearch.test.ts index 6a2227ba0ae..71b2546f9a7 100644 --- a/src/vs/workbench/services/search/test/node/ripgrepTextSearch.test.ts +++ b/src/vs/workbench/services/search/test/node/ripgrepTextSearch.test.ts @@ -11,7 +11,7 @@ import assert = require('assert'); import * as arrays from 'vs/base/common/arrays'; import platform = require('vs/base/common/platform'); -import { RipgrepParser, getAbsoluteGlob } from 'vs/workbench/services/search/node/ripgrepTextSearch'; +import { RipgrepParser, getAbsoluteGlob, fixDriveC } from 'vs/workbench/services/search/node/ripgrepTextSearch'; import { ISerializedFileMatch } from 'vs/workbench/services/search/node/search'; @@ -177,7 +177,7 @@ suite('RipgrepParser', () => { suite('RipgrepParser - etc', () => { function testGetAbsGlob(params: string[]): void { const [folder, glob, expectedResult] = params; - assert.equal(getAbsoluteGlob(folder, glob), expectedResult, JSON.stringify(params)); + assert.equal(fixDriveC(getAbsoluteGlob(folder, glob)), expectedResult, JSON.stringify(params)); } test('getAbsoluteGlob_win', () => { diff --git a/src/vs/workbench/services/search/test/node/textSearch.integrationTest.ts b/src/vs/workbench/services/search/test/node/textSearch.integrationTest.ts index 00dc96da0c6..e0c421a55e6 100644 --- a/src/vs/workbench/services/search/test/node/textSearch.integrationTest.ts +++ b/src/vs/workbench/services/search/test/node/textSearch.integrationTest.ts @@ -50,7 +50,7 @@ function doLegacySearchTest(config: IRawSearch, expectedResultCount: number | Fu if (typeof expectedResultCount === 'function') { assert(expectedResultCount(c)); } else { - assert.equal(c, expectedResultCount); + assert.equal(c, expectedResultCount, 'legacy'); } } catch (e) { reject(e); @@ -76,7 +76,7 @@ function doRipgrepSearchTest(config: IRawSearch, expectedResultCount: number): T if (typeof expectedResultCount === 'function') { assert(expectedResultCount(c)); } else { - assert.equal(c, expectedResultCount); + assert.equal(c, expectedResultCount, 'rg'); } } catch (e) { reject(e); @@ -88,8 +88,7 @@ function doRipgrepSearchTest(config: IRawSearch, expectedResultCount: number): T } function doSearchTest(config: IRawSearch, expectedResultCount: number, done) { - return doLegacySearchTest(config, expectedResultCount) - .then(() => doRipgrepSearchTest(config, expectedResultCount)) + return doRipgrepSearchTest(config, expectedResultCount) .then(done, done); } @@ -188,6 +187,17 @@ suite('Search-integration', function () { doSearchTest(config, 382, done); }); + test('Text: sibling exclude', function (done: () => void) { + const config: any = { + folderQueries: ROOT_FOLDER_QUERY, + contentPattern: { pattern: 'm' }, + includePattern: makeExpression('**/site*'), + excludePattern: { '*.css': { when: '$(basename).less' } } + }; + + doSearchTest(config, 1, done); + }); + test('Text: e (with includes and exclude)', function (done: () => void) { const config: any = { folderQueries: ROOT_FOLDER_QUERY, From 48c022fbc010a914bbe0cd40c22005d0f63479a2 Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Tue, 25 Jul 2017 15:50:07 -0700 Subject: [PATCH 077/136] Fix #30266 --- src/vs/workbench/services/search/node/ripgrepTextSearch.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/services/search/node/ripgrepTextSearch.ts b/src/vs/workbench/services/search/node/ripgrepTextSearch.ts index 607ea849557..ae8e9135aee 100644 --- a/src/vs/workbench/services/search/node/ripgrepTextSearch.ts +++ b/src/vs/workbench/services/search/node/ripgrepTextSearch.ts @@ -442,9 +442,14 @@ function globExprsToRgGlobs(patterns: glob.IExpression, folder: string): IRgGlob * Exported for testing */ export function getAbsoluteGlob(folder: string, key: string): string { - return paths.isAbsolute(key) ? + let absolute = paths.isAbsolute(key) ? key : path.join(folder, key); + + absolute = strings.rtrim(absolute, '\\'); + absolute = strings.rtrim(absolute, '/'); + + return absolute; } export function fixDriveC(path: string): string { From 4ea86cf148ddd2e9d235e06da0eb017ef42133f5 Mon Sep 17 00:00:00 2001 From: Ramya Achutha Rao Date: Tue, 25 Jul 2017 15:29:44 -0700 Subject: [PATCH 078/136] Updating name of the command in tests --- extensions/emmet/src/test/reflectCssValue.test.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/extensions/emmet/src/test/reflectCssValue.test.ts b/extensions/emmet/src/test/reflectCssValue.test.ts index a3601117706..a4747ff1397 100644 --- a/extensions/emmet/src/test/reflectCssValue.test.ts +++ b/extensions/emmet/src/test/reflectCssValue.test.ts @@ -41,7 +41,7 @@ suite('Tests for Emmet: Reflect CSS Value command', () => { test('Reflect Css Value in css file', function (): any { return withRandomFileEditor(cssContents, '.css', (editor, doc) => { editor.selections = [new Selection(5, 10, 5, 10)]; - return commands.executeCommand('emmet.reflectCssValue').then(() => { + return commands.executeCommand('emmet.reflectCSSValue').then(() => { assert.equal(doc.getText(), cssContents.replace(/\(50deg\)/g, '(20deg)')); return Promise.resolve(); }); @@ -51,7 +51,7 @@ suite('Tests for Emmet: Reflect CSS Value command', () => { test('Reflect Css Value in html file', function (): any { return withRandomFileEditor(htmlContents, '.html', (editor, doc) => { editor.selections = [new Selection(7, 20, 7, 20)]; - return commands.executeCommand('emmet.reflectCssValue').then(() => { + return commands.executeCommand('emmet.reflectCSSValue').then(() => { assert.equal(doc.getText(), htmlContents.replace(/\(50deg\)/g, '(20deg)')); return Promise.resolve(); }); From 745e05d0791c3725ded408d91c99aaf65c23a8e0 Mon Sep 17 00:00:00 2001 From: Ramya Achutha Rao Date: Tue, 25 Jul 2017 15:38:28 -0700 Subject: [PATCH 079/136] Emmet Tests for Next/Prev Item/Editpoint for html --- .../emmet/src/test/editSelectItem.test.ts | 103 ++++++++++++++++++ 1 file changed, 103 insertions(+) create mode 100644 extensions/emmet/src/test/editSelectItem.test.ts diff --git a/extensions/emmet/src/test/editSelectItem.test.ts b/extensions/emmet/src/test/editSelectItem.test.ts new file mode 100644 index 00000000000..2dc74590c30 --- /dev/null +++ b/extensions/emmet/src/test/editSelectItem.test.ts @@ -0,0 +1,103 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as assert from 'assert'; +import { Selection } from 'vscode'; +import { withRandomFileEditor, closeAllEditors } from './testUtils'; +import { fetchEditPoint } from '../editPoint'; +import { fetchSelectItem } from '../selectItem'; + +suite('Tests for Next/Previous Select/Edit point actions', () => { + teardown(closeAllEditors); + + const htmlContents = ` + + + + + + + + +
+ +
+
+ +
+ + +`; + + test('Emmet Next/Prev Edit point in html file', function (): any { + return withRandomFileEditor(htmlContents, '.html', (editor, doc) => { + editor.selections = [new Selection(1, 5, 1, 5)]; + + let expectedNextEditPoints: [number, number][] = [[4, 16], [6, 8], [10, 2], [20, 0]]; + expectedNextEditPoints.forEach(([line, col]) => { + fetchEditPoint('next'); + testSelection(editor.selection, line, col); + }); + + let expectedPrevEditPoints = [[10, 2], [6, 8], [4, 16], [0, 0]]; + expectedPrevEditPoints.forEach(([line, col]) => { + fetchEditPoint('prev'); + testSelection(editor.selection, line, col); + }); + + return Promise.resolve(); + }); + }); + + test('Emmet Select Next/Prev Item in html file', function (): any { + return withRandomFileEditor(htmlContents, '.html', (editor, doc) => { + editor.selections = [new Selection(2, 2, 2, 2)]; + + let expectedNextItemPoints: [number, number, number][] = [ + [2, 1, 5], // html + [2, 6, 15], // lang="en" + [2, 12, 14], // en + [3, 1, 5], // head + [4, 2, 6], // meta + [4, 7, 17], // charset="" + [5, 2, 6], // meta + [5, 7, 22], // name="viewport" + [5, 13, 21], // viewport + [5, 23, 70], // content="width=device-width, initial-scale=1.0" + [5, 32, 69], // width=device-width, initial-scale=1.0 + [5, 32, 51], // width=device-width, + [5, 52, 69], // initial-scale=1.0 + [6, 2, 7] // title + ]; + expectedNextItemPoints.forEach(([line, colstart, colend]) => { + fetchSelectItem('next'); + testSelection(editor.selection, line, colstart, colend); + }); + + editor.selections = [new Selection(6, 15, 6, 15)]; + expectedNextItemPoints.reverse().forEach(([line, colstart, colend]) => { + fetchSelectItem('prev'); + testSelection(editor.selection, line, colstart, colend); + }); + + return Promise.resolve(); + }); + }); + +}); + +function testSelection(selection: Selection, line: number, startChar: number, endChar?: number) { + assert.equal(selection.isSingleLine, true); + assert.equal(selection.anchor.line, line); + assert.equal(selection.anchor.character, startChar); + if (!endChar) { + assert.equal(selection.isEmpty, true); + } else { + assert.equal(selection.active.character, endChar); + } +} \ No newline at end of file From 8c8220572444401db6db19d0919eb833a4eb7569 Mon Sep 17 00:00:00 2001 From: Ramya Achutha Rao Date: Tue, 25 Jul 2017 15:59:31 -0700 Subject: [PATCH 080/136] Emmet Tests for Next/Prev Item for css --- .../emmet/src/test/editSelectItem.test.ts | 42 +++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/extensions/emmet/src/test/editSelectItem.test.ts b/extensions/emmet/src/test/editSelectItem.test.ts index 2dc74590c30..2bf839d23b3 100644 --- a/extensions/emmet/src/test/editSelectItem.test.ts +++ b/extensions/emmet/src/test/editSelectItem.test.ts @@ -12,6 +12,17 @@ import { fetchSelectItem } from '../selectItem'; suite('Tests for Next/Previous Select/Edit point actions', () => { teardown(closeAllEditors); + const cssContents = ` +.boo { + margin: 20px 10px; + background-image: url('tryme.png'); +} + +.boo .hoo { + margin: 10px; +} +`; + const htmlContents = ` @@ -89,6 +100,37 @@ suite('Tests for Next/Previous Select/Edit point actions', () => { }); }); + test('Emmet Select Next/Prev Item in css file', function (): any { + return withRandomFileEditor(cssContents, '.css', (editor, doc) => { + editor.selections = [new Selection(0, 0, 0, 0)]; + + let expectedNextItemPoints: [number, number, number][] = [ + [1, 0, 4], // .boo + [2, 1, 19], // margin: 20px 10px; + [2, 9, 18], // 20px 10px + [2, 9, 13], // 20px + [2, 14, 18], // 10px + [3, 1, 36], // background-image: url('tryme.png'); + [3, 19, 35], // url('tryme.png') + [6, 0, 9], // .boo .hoo + [7, 1, 14], // margin: 10px; + [7, 9, 13], // 10px + ]; + expectedNextItemPoints.forEach(([line, colstart, colend]) => { + fetchSelectItem('next'); + testSelection(editor.selection, line, colstart, colend); + }); + + editor.selections = [new Selection(9, 0, 9, 0)]; + expectedNextItemPoints.reverse().forEach(([line, colstart, colend]) => { + fetchSelectItem('prev'); + testSelection(editor.selection, line, colstart, colend); + }); + + return Promise.resolve(); + }); + }); + }); function testSelection(selection: Selection, line: number, startChar: number, endChar?: number) { From 0a4fa1e7a6e8020f61617a023b999ce1a3c9cf23 Mon Sep 17 00:00:00 2001 From: Ramya Achutha Rao Date: Tue, 25 Jul 2017 16:06:30 -0700 Subject: [PATCH 081/136] Emmet Tests for Next/Prev Item for scss with nested rules --- .../emmet/src/test/editSelectItem.test.ts | 42 +++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/extensions/emmet/src/test/editSelectItem.test.ts b/extensions/emmet/src/test/editSelectItem.test.ts index 2bf839d23b3..f1994585976 100644 --- a/extensions/emmet/src/test/editSelectItem.test.ts +++ b/extensions/emmet/src/test/editSelectItem.test.ts @@ -21,6 +21,17 @@ suite('Tests for Next/Previous Select/Edit point actions', () => { .boo .hoo { margin: 10px; } +`; + + const scssContents = ` +.boo { + margin: 20px 10px; + background-image: url('tryme.png'); + + .boo .hoo { + margin: 10px; + } +} `; const htmlContents = ` @@ -131,6 +142,37 @@ suite('Tests for Next/Previous Select/Edit point actions', () => { }); }); + test('Emmet Select Next/Prev Item in scss file with nested rules', function (): any { + return withRandomFileEditor(scssContents, '.scss', (editor, doc) => { + editor.selections = [new Selection(0, 0, 0, 0)]; + + let expectedNextItemPoints: [number, number, number][] = [ + [1, 0, 4], // .boo + [2, 1, 19], // margin: 20px 10px; + [2, 9, 18], // 20px 10px + [2, 9, 13], // 20px + [2, 14, 18], // 10px + [3, 1, 36], // background-image: url('tryme.png'); + [3, 19, 35], // url('tryme.png') + [5, 1, 10], // .boo .hoo + [6, 2, 15], // margin: 10px; + [6, 10, 14], // 10px + ]; + expectedNextItemPoints.forEach(([line, colstart, colend]) => { + fetchSelectItem('next'); + testSelection(editor.selection, line, colstart, colend); + }); + + editor.selections = [new Selection(8, 0, 8, 0)]; + expectedNextItemPoints.reverse().forEach(([line, colstart, colend]) => { + fetchSelectItem('prev'); + testSelection(editor.selection, line, colstart, colend); + }); + + return Promise.resolve(); + }); + }); + }); function testSelection(selection: Selection, line: number, startChar: number, endChar?: number) { From 6030187b6cfef6cb6de93e65ae5bb6d8ee4c3868 Mon Sep 17 00:00:00 2001 From: Ramya Achutha Rao Date: Tue, 25 Jul 2017 17:16:10 -0700 Subject: [PATCH 082/136] Tests for Emmet Balance In/Out --- extensions/emmet/src/balance.ts | 11 +-- .../emmet/src/test/editSelectItem.test.ts | 70 +++++++++++++++---- 2 files changed, 63 insertions(+), 18 deletions(-) diff --git a/extensions/emmet/src/balance.ts b/extensions/emmet/src/balance.ts index f3fb4e47984..22474be22dd 100644 --- a/extensions/emmet/src/balance.ts +++ b/extensions/emmet/src/balance.ts @@ -59,15 +59,18 @@ function getRangeToBalanceOut(document: vscode.TextDocument, selection: vscode.S } function getRangeToBalanceIn(document: vscode.TextDocument, selection: vscode.Selection, rootNode: HtmlNode): vscode.Selection { - let nodeToBalance = getNode(rootNode, selection.start); + let nodeToBalance = getNode(rootNode, selection.start, true); if (!nodeToBalance) { return; } + if (selection.start.isEqual(nodeToBalance.start) + && selection.end.isEqual(nodeToBalance.end) + && nodeToBalance.close) { + return new vscode.Selection(nodeToBalance.open.end, nodeToBalance.close.start); + } + if (!nodeToBalance.firstChild) { - if (nodeToBalance.close) { - return new vscode.Selection(nodeToBalance.open.end, nodeToBalance.close.start); - } return; } diff --git a/extensions/emmet/src/test/editSelectItem.test.ts b/extensions/emmet/src/test/editSelectItem.test.ts index f1994585976..33511a66d91 100644 --- a/extensions/emmet/src/test/editSelectItem.test.ts +++ b/extensions/emmet/src/test/editSelectItem.test.ts @@ -8,6 +8,7 @@ import { Selection } from 'vscode'; import { withRandomFileEditor, closeAllEditors } from './testUtils'; import { fetchEditPoint } from '../editPoint'; import { fetchSelectItem } from '../selectItem'; +import { balanceOut, balanceIn } from '../balance'; suite('Tests for Next/Previous Select/Edit point actions', () => { teardown(closeAllEditors); @@ -48,8 +49,8 @@ suite('Tests for Next/Previous Select/Edit point actions', () => {
@@ -63,13 +64,13 @@ suite('Tests for Next/Previous Select/Edit point actions', () => { let expectedNextEditPoints: [number, number][] = [[4, 16], [6, 8], [10, 2], [20, 0]]; expectedNextEditPoints.forEach(([line, col]) => { fetchEditPoint('next'); - testSelection(editor.selection, line, col); + testSelection(editor.selection, col, line); }); let expectedPrevEditPoints = [[10, 2], [6, 8], [4, 16], [0, 0]]; expectedPrevEditPoints.forEach(([line, col]) => { fetchEditPoint('prev'); - testSelection(editor.selection, line, col); + testSelection(editor.selection, col, line); }); return Promise.resolve(); @@ -98,13 +99,13 @@ suite('Tests for Next/Previous Select/Edit point actions', () => { ]; expectedNextItemPoints.forEach(([line, colstart, colend]) => { fetchSelectItem('next'); - testSelection(editor.selection, line, colstart, colend); + testSelection(editor.selection, colstart, line, colend); }); editor.selections = [new Selection(6, 15, 6, 15)]; expectedNextItemPoints.reverse().forEach(([line, colstart, colend]) => { fetchSelectItem('prev'); - testSelection(editor.selection, line, colstart, colend); + testSelection(editor.selection, colstart, line, colend); }); return Promise.resolve(); @@ -129,13 +130,13 @@ suite('Tests for Next/Previous Select/Edit point actions', () => { ]; expectedNextItemPoints.forEach(([line, colstart, colend]) => { fetchSelectItem('next'); - testSelection(editor.selection, line, colstart, colend); + testSelection(editor.selection, colstart, line, colend); }); editor.selections = [new Selection(9, 0, 9, 0)]; expectedNextItemPoints.reverse().forEach(([line, colstart, colend]) => { fetchSelectItem('prev'); - testSelection(editor.selection, line, colstart, colend); + testSelection(editor.selection, colstart, line, colend); }); return Promise.resolve(); @@ -160,13 +161,49 @@ suite('Tests for Next/Previous Select/Edit point actions', () => { ]; expectedNextItemPoints.forEach(([line, colstart, colend]) => { fetchSelectItem('next'); - testSelection(editor.selection, line, colstart, colend); + testSelection(editor.selection, colstart, line, colend); }); editor.selections = [new Selection(8, 0, 8, 0)]; expectedNextItemPoints.reverse().forEach(([line, colstart, colend]) => { fetchSelectItem('prev'); - testSelection(editor.selection, line, colstart, colend); + testSelection(editor.selection, colstart, line, colend); + }); + + return Promise.resolve(); + }); + }); + + test('Emmet Balance Out in html file', function (): any { + return withRandomFileEditor(htmlContents, 'html', (editor, doc) => { + + editor.selections = [new Selection(14, 6, 14, 10)]; + let expectedBalanceOutRanges: [number, number, number, number][] = [ + [14, 3, 14, 32], //
  • Item 1
  • + [13, 23, 16, 2], // inner contents of