Merge from vscode 011858832762aaff245b2336fb1c38166e7a10fb (#4663)

This commit is contained in:
Anthony Dresser
2019-03-22 13:07:54 -07:00
committed by GitHub
parent f5c9174c2f
commit 4a87a24235
296 changed files with 2531 additions and 2472 deletions

View File

@@ -1,6 +1,6 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { localize } from 'vs/nls';
@@ -123,7 +123,7 @@ registerEditorAction(class extends EditorAction {
alias: 'Call Hierarchy',
menuOpts: {
group: 'navigation',
order: 111
order: 1.48
},
kbOpts: {
kbExpr: EditorContextKeys.editorTextFocus,

View File

@@ -1,6 +1,6 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import 'vs/css!./media/callHierarchy';
@@ -11,7 +11,7 @@ import { CallHierarchyProvider, CallHierarchyDirection, CallHierarchyItem } from
import { WorkbenchAsyncDataTree } from 'vs/platform/list/browser/listService';
import { FuzzyScore } from 'vs/base/common/filters';
import * as callHTree from 'vs/workbench/contrib/callHierarchy/browser/callHierarchyTree';
import { IAsyncDataTreeOptions } from 'vs/base/browser/ui/tree/asyncDataTree';
import { IAsyncDataTreeOptions, IAsyncDataTreeViewState } from 'vs/base/browser/ui/tree/asyncDataTree';
import { localize } from 'vs/nls';
import { ScrollType } from 'vs/editor/common/editorCommon';
import { IRange, Range } from 'vs/editor/common/core/range';
@@ -40,25 +40,29 @@ const enum State {
Data = 'data'
}
class ToggleHierarchyDirectionAction extends Action {
class ChangeHierarchyDirectionAction extends Action {
constructor(public direction: () => CallHierarchyDirection, callback: () => void) {
super('toggle.dir', undefined, 'call-hierarchy-toggle', true, () => {
callback();
this._update();
constructor(direction: CallHierarchyDirection, updateDirection: (direction: CallHierarchyDirection) => void) {
super('', undefined, '', true, () => {
if (direction === CallHierarchyDirection.CallsTo) {
direction = CallHierarchyDirection.CallsFrom;
} else {
direction = CallHierarchyDirection.CallsTo;
}
updateDirection(direction);
update();
return Promise.resolve();
});
this._update();
}
private _update() {
if (this.direction() === CallHierarchyDirection.CallsFrom) {
this.label = localize('toggle.from', "Calls From...");
this.checked = true;
} else {
this.label = localize('toggle.to', "Calls To...");
this.checked = false;
}
const update = () => {
if (direction === CallHierarchyDirection.CallsFrom) {
this.label = localize('toggle.from', "Showing Calls");
this.class = 'calls-from';
} else {
this.label = localize('toggle.to', "Showing Callers");
this.class = 'calls-to';
}
};
update();
}
}
@@ -86,11 +90,12 @@ class LayoutInfo {
export class CallHierarchyTreePeekWidget extends PeekViewWidget {
private _toggleDirection: ToggleHierarchyDirectionAction;
private _changeDirectionAction: ChangeHierarchyDirectionAction;
private _parent: HTMLElement;
private _message: HTMLElement;
private _splitView: SplitView;
private _tree: WorkbenchAsyncDataTree<CallHierarchyItem, callHTree.Call, FuzzyScore>;
private _treeViewStates = new Map<CallHierarchyDirection, IAsyncDataTreeViewState>();
private _editor: EmbeddedCodeEditorWidget;
private _dim: Dimension;
private _layoutInfo: LayoutInfo;
@@ -311,7 +316,7 @@ export class CallHierarchyTreePeekWidget extends PeekViewWidget {
this._tree.onDidChangeSelection(e => {
const [element] = e.elements;
// don't close on click
if (element && isNonEmptyArray(element.locations) && !(e.browserEvent instanceof MouseEvent)) {
if (element && isNonEmptyArray(element.locations) && e.browserEvent instanceof KeyboardEvent) {
this.dispose();
this._editorService.openEditor({
resource: element.item.uri,
@@ -338,7 +343,8 @@ export class CallHierarchyTreePeekWidget extends PeekViewWidget {
async showItem(item: CallHierarchyItem): Promise<void> {
this._show();
await this._tree.setInput(item);
const viewState = this._treeViewStates.get(this._direction);
await this._tree.setInput(item, viewState);
const [root] = this._tree.getNode(item).children;
await this._tree.expand(root.element as callHTree.Call);
@@ -352,24 +358,26 @@ export class CallHierarchyTreePeekWidget extends PeekViewWidget {
} else {
this._parent.dataset['state'] = State.Data;
this._tree.domFocus();
this._tree.setFocus([firstChild]);
if (!viewState) {
this._tree.setFocus([firstChild]);
}
this.setTitle(
item.name,
item.detail || this._labelService.getUriLabel(item.uri, { relative: true }),
);
}
if (!this._toggleDirection) {
this._toggleDirection = new ToggleHierarchyDirectionAction(
() => this._direction,
() => {
let newDirection = this._direction === CallHierarchyDirection.CallsFrom ? CallHierarchyDirection.CallsTo : CallHierarchyDirection.CallsFrom;
if (!this._changeDirectionAction) {
const changeDirection = (newDirection: CallHierarchyDirection) => {
if (this._direction !== newDirection) {
this._treeViewStates.set(this._direction, this._tree.getViewState());
this._direction = newDirection;
this.showItem(item);
}
);
this._actionbarWidget.push(this._toggleDirection, { label: false, icon: true });
this._disposables.push(this._toggleDirection);
};
this._changeDirectionAction = new ChangeHierarchyDirectionAction(this._direction, changeDirection);
this._disposables.push(this._changeDirectionAction);
this._actionbarWidget.push(this._changeDirectionAction, { icon: true, label: false });
}
}

View File

@@ -1,6 +1,6 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { IAsyncDataSource, ITreeRenderer, ITreeNode } from 'vs/base/browser/ui/tree/tree';
@@ -12,6 +12,7 @@ import { IconLabel } from 'vs/base/browser/ui/iconLabel/iconLabel';
import { symbolKindToCssClass, Location } from 'vs/editor/common/modes';
import { ILabelService } from 'vs/platform/label/common/label';
import { Range } from 'vs/editor/common/core/range';
import { hash } from 'vs/base/common/hash';
export class Call {
constructor(
@@ -52,7 +53,7 @@ export class SingleDirectionDataSource implements IAsyncDataSource<CallHierarchy
export class IdentityProvider implements IIdentityProvider<Call> {
getId(element: Call): { toString(): string; } {
return element.item._id;
return hash(element.item.uri.toString(), hash(JSON.stringify(element.item.range)));
}
}

View File

@@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"><style type="text/css">.icon-canvas-transparent{opacity:0;fill:#F6F6F6;} .icon-vs-out{fill:#F6F6F6;} .icon-vs-bg{fill:#424242;} .icon-vs-action-blue{fill:#00539C;}</style><path class="icon-canvas-transparent" d="M16 16h-16v-16h16v16z" id="canvas"/><path class="icon-vs-out" d="M15.989 8l-2 2h2v4h-2l2 2h-6l-3.989-3.989v2.287l-.497.289c-.464.27-.983.413-1.503.413-1.654 0-3-1.346-3-3v-6c0-1.654 1.346-3 3-3 .52 0 1.039.143 1.503.413l.497.289v4.298h-2v2h2v1.989l3.989-3.989h-3l2-2h-2v-4h2l-2-2h6l3 3 .011 1.989-3.011 3.011h3z" id="outline"/><path class="icon-vs-bg" d="M4 4c.366 0 .705.105 1 .277v2.723h-2v4h2v2.723c-.295.171-.634.277-1 .277-1.104 0-2-.896-2-2v-6c0-1.104.896-2 2-2z" id="iconBg"/><path class="icon-vs-action-blue" d="M11.989 1h-2l2 2h-4v2h4l-2 2h2l3-3-3-3zm-4 11l3 3h2l-2-2h4v-2h-4l2-2h-2l-3 3z" id="colorAction"/></svg>

Before

Width:  |  Height:  |  Size: 898 B

View File

@@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"><style>.icon-canvas-transparent{opacity:0;fill:#f6f6f6}.icon-vs-out{fill:#f6f6f6}.icon-vs-bg{fill:#424242}.icon-vs-action-blue{fill:#00539c}</style><path class="icon-canvas-transparent" d="M16 16H0V0h16v16z" id="canvas"/><path class="icon-vs-out" d="M15.989 8l-2 2h2v4h-2l2 2h-6L6 12.011v2.287l-.497.289C5.039 14.857 4.52 15 4 15c-1.654 0-3-1.346-3-3V6c0-1.654 1.346-3 3-3 .52 0 1.039.143 1.503.413L6 3.702V8H4v2h2v1.989L9.989 8h-3l2-2h-2V2h2l-2-2h6l3 3L16 4.989 12.989 8h3z" id="outline"/><path class="icon-vs-bg" d="M4 4c.366 0 .705.105 1 .277V7H3v4h2v2.723A1.987 1.987 0 0 1 4 14a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2z" id="iconBg"/><path class="icon-vs-action-blue" d="M11.989 1h-2l2 2h-4v2h4l-2 2h2l3-3-3-3zm-4 11l3 3h2l-2-2h4v-2h-4l2-2h-2l-3 3z" id="colorAction"/></svg>

Before

Width:  |  Height:  |  Size: 831 B

View File

@@ -1,6 +1,6 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
.monaco-workbench .call-hierarchy .results,
@@ -18,16 +18,18 @@
padding-top: 3em;
}
.monaco-workbench .call-hierarchy-toggle {
background-image: url(CallerOrCalleeView_16x.svg);
.monaco-workbench .action-label.calls-to {
background-image: url(files_CallTo_CallTo_16x.svg);
background-size: 14px 14px;
background-repeat: no-repeat;
background-position: left center;
}
.vs-dark .monaco-workbench .call-hierarchy-toggle,
.hc-dark .monaco-workbench .call-hierarchy-toggle {
background-image: url(CallerOrCalleeView_16x_dark.svg);
.monaco-workbench .action-label.calls-from {
background-image: url(files_CallFrom_CallFrom_16x.svg);
background-size: 14px 14px;
background-repeat: no-repeat;
background-position: left center;
}
.monaco-workbench .call-hierarchy .monaco-split-view2.horizontal > .split-view-container > .split-view-view{

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><style>.icon-canvas-transparent{opacity:0;fill:#f6f6f6}.icon-vs-out{fill:#f6f6f6}.icon-vs-bg{fill:#424242}</style><path class="icon-canvas-transparent" d="M16 16H0V0h16v16z" id="canvas"/><path class="icon-vs-out" d="M12 6v4h2v4.987l-.398.3A3.46 3.46 0 0 1 11.5 16C9.57 16 8 14.43 8 12.5V9.006H6.344l1.318 1.322-2.119 2.119L1 7.92v-.798l4.55-4.55 2.12 2.12-1.309 1.313H8V3.5C8 1.57 9.57 0 11.5 0a3.46 3.46 0 0 1 2.102.713l.398.3V6h-2z" id="outline"/><path class="icon-vs-bg" d="M11 10.051V11h2v3.489a2.476 2.476 0 0 1-1.5.511A2.5 2.5 0 0 1 9 12.5v-9A2.5 2.5 0 0 1 11.5 1c.565 0 1.081.194 1.5.511V5h-2v5.051zM6.257 4.693l-.707-.707L2.016 7.52l3.526 3.514.707-.707-2.314-2.322h4.12v-1H3.953l2.304-2.312z" id="iconBg"/></svg>

After

Width:  |  Height:  |  Size: 781 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><style>.icon-canvas-transparent{opacity:0;fill:#f6f6f6}.icon-vs-out{fill:#f6f6f6}.icon-vs-bg{fill:#424242}</style><path class="icon-canvas-transparent" d="M16 16H0V0h16v16z" id="canvas"/><path class="icon-vs-out" d="M3.727 9.006H1.016v-3h2.693L2.4 4.692l2.12-2.12L8 6.052V3.5C8 1.57 9.57 0 11.5 0c.758 0 1.485.247 2.103.713l.362.273v14.028l-.362.273A3.465 3.465 0 0 1 11.5 16C9.57 16 8 14.43 8 12.5V8.986l-3.473 3.461-2.119-2.119 1.319-1.322z" id="outline"/><path class="icon-vs-bg" d="M11 10.051V11h2v3.489a2.476 2.476 0 0 1-1.5.511A2.5 2.5 0 0 1 9 12.5v-9A2.5 2.5 0 0 1 11.5 1c.565 0 1.081.194 1.5.511V5h-2v5.051zM3.813 4.693l2.305 2.312H2.016v1h4.12l-2.314 2.322.707.707L8.055 7.52 4.52 3.986l-.707.707z" id="iconBg"/></svg>

After

Width:  |  Height:  |  Size: 787 B

View File

@@ -1,6 +1,6 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { IPosition } from 'vs/editor/common/core/position';

View File

@@ -1,6 +1,6 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import './menuPreventer';

View File

@@ -60,7 +60,7 @@ export class ReviewZoneWidget extends ZoneWidget implements ICommentThreadWidget
private _resizeObserver: any;
private _onDidClose = new Emitter<ReviewZoneWidget | undefined>();
private _onDidCreateThread = new Emitter<ReviewZoneWidget>();
private _isCollapsed;
private _isCollapsed: boolean;
private _collapseAction: Action;
private _commentGlyph?: CommentGlyphWidget;
private _submitActionsDisposables: IDisposable[];

View File

@@ -95,7 +95,7 @@ export class DebugToolbar extends Themable implements IWorkbenchContribution {
return new MenuItemActionItem(action, this.keybindingService, this.notificationService, contextMenuService);
}
return null;
return undefined;
}
}));

View File

@@ -129,7 +129,7 @@ export class DebugViewlet extends ViewContainerViewlet {
return [this.selectAndStartAction, this.configureAction, this.toggleReplAction];
}
getActionItem(action: IAction): IActionItem | null {
getActionItem(action: IAction): IActionItem | undefined {
if (action.id === StartAction.ID) {
this.startDebugActionItem = this.instantiationService.createInstance(StartDebugActionItem, null, action);
return this.startDebugActionItem;
@@ -141,7 +141,7 @@ export class DebugViewlet extends ViewContainerViewlet {
return new MenuItemActionItem(action, this.keybindingService, this.notificationService, this.contextMenuService);
}
return null;
return undefined;
}
focusView(id: string): void {

View File

@@ -33,9 +33,13 @@ import { WorkbenchAsyncDataTree, TreeResourceNavigator2 } from 'vs/platform/list
import { dispose } from 'vs/base/common/lifecycle';
import { createMatches, FuzzyScore } from 'vs/base/common/filters';
import { DebugContentProvider } from 'vs/workbench/contrib/debug/common/debugContentProvider';
import { ILabelService } from 'vs/platform/label/common/label';
const SMART = true;
// RFC 2396, Appendix A: https://www.ietf.org/rfc/rfc2396.txt
const URI_SCHEMA_PATTERN = /^[a-zA-Z][a-zA-Z0-9\+\-\.]+:/;
type LoadedScriptsItem = BaseTreeItem;
class BaseTreeItem {
@@ -217,7 +221,7 @@ class RootFolderTreeItem extends BaseTreeItem {
class RootTreeItem extends BaseTreeItem {
constructor(private _debugModel: IDebugModel, private _environmentService: IEnvironmentService, private _contextService: IWorkspaceContextService) {
constructor(private _debugModel: IDebugModel, private _environmentService: IEnvironmentService, private _contextService: IWorkspaceContextService, private _labelService: ILabelService) {
super(undefined, 'Root');
this._debugModel.getSessions().forEach(session => {
this.add(session);
@@ -225,7 +229,7 @@ class RootTreeItem extends BaseTreeItem {
}
add(session: IDebugSession): SessionTreeItem {
return this.createIfNeeded(session.getId(), () => new SessionTreeItem(this, session, this._environmentService, this._contextService));
return this.createIfNeeded(session.getId(), () => new SessionTreeItem(this._labelService, this, session, this._environmentService, this._contextService));
}
find(session: IDebugSession): SessionTreeItem {
@@ -240,9 +244,11 @@ class SessionTreeItem extends BaseTreeItem {
private _session: IDebugSession;
private _initialized: boolean;
private _map: Map<string, BaseTreeItem>;
private _labelService: ILabelService;
constructor(parent: BaseTreeItem, session: IDebugSession, private _environmentService: IEnvironmentService, private rootProvider: IWorkspaceContextService) {
constructor(labelService: ILabelService, parent: BaseTreeItem, session: IDebugSession, private _environmentService: IEnvironmentService, private rootProvider: IWorkspaceContextService) {
super(parent, session.getLabel());
this._labelService = labelService;
this._initialized = false;
this._session = session;
this._map = new Map();
@@ -309,6 +315,10 @@ class SessionTreeItem extends BaseTreeItem {
return;
}
if (this._labelService && URI_SCHEMA_PATTERN.test(path)) {
path = this._labelService.getUriLabel(URI.parse(path));
}
const match = SessionTreeItem.URL_REGEXP.exec(path);
if (match && match.length === 3) {
url = match[1];
@@ -390,6 +400,7 @@ export class LoadedScriptsView extends ViewletPanel {
@IWorkspaceContextService private readonly contextService: IWorkspaceContextService,
@IEnvironmentService private readonly environmentService: IEnvironmentService,
@IDebugService private readonly debugService: IDebugService,
@ILabelService private readonly labelService: ILabelService
) {
super({ ...(options as IViewletPanelOptions), ariaHeaderLabel: nls.localize('loadedScriptsSection', "Loaded Scripts Section") }, keybindingService, contextMenuService, configurationService);
this.loadedScriptsItemType = CONTEXT_LOADED_SCRIPTS_ITEM_TYPE.bindTo(contextKeyService);
@@ -403,7 +414,7 @@ export class LoadedScriptsView extends ViewletPanel {
this.filter = new LoadedScriptsFilter();
const root = new RootTreeItem(this.debugService.getModel(), this.environmentService, this.contextService);
const root = new RootTreeItem(this.debugService.getModel(), this.environmentService, this.contextService, this.labelService);
this.treeLabels = this.instantiationService.createInstance(ResourceLabels, { onDidChangeVisibility: this.onDidChangeBodyVisibility } as IResourceLabelsContainer);
this.disposables.push(this.treeLabels);

View File

@@ -309,12 +309,12 @@ export class Repl extends Panel implements IPrivateReplService, IHistoryNavigati
this.replInput.focus();
}
getActionItem(action: IAction): IActionItem | null {
getActionItem(action: IAction): IActionItem | undefined {
if (action.id === SelectReplAction.ID) {
return this.instantiationService.createInstance(SelectReplActionItem, this.selectReplAction);
}
return null;
return undefined;
}
getActions(): IAction[] {

View File

@@ -1,6 +1,6 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { Emitter, Event } from 'vs/base/common/event';

View File

@@ -40,7 +40,7 @@ suite('Debug - Source', () => {
});
test('get encoded debug data', () => {
const checkData = (uri: uri, expectedName, expectedPath, expectedSourceReference, expectedSessionId) => {
const checkData = (uri: uri, expectedName: string, expectedPath: string, expectedSourceReference: number | undefined, expectedSessionId?: number) => {
let { name, path, sourceReference, sessionId } = Source.getEncodedDebugData(uri);
assert.equal(name, expectedName);
assert.equal(path, expectedPath);

View File

@@ -27,7 +27,7 @@ import { LanguageId, LanguageIdentifier } from 'vs/editor/common/modes';
//
class MockGrammarContributions implements IGrammarContributions {
private scopeName;
private scopeName: string;
constructor(scopeName: string) {
this.scopeName = scopeName;

View File

@@ -7,9 +7,9 @@ import * as assert from 'assert';
import { Emitter } from 'vs/base/common/event';
import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock';
import { ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle';
import { INotificationService, IPromptChoice, Severity } from 'vs/platform/notification/common/notification';
import { INotificationService, IPromptChoice, Severity, IPromptOptions } from 'vs/platform/notification/common/notification';
import { TestNotificationService } from 'vs/platform/notification/test/common/testNotificationService';
import { IStorageService } from 'vs/platform/storage/common/storage';
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { NullTelemetryService } from 'vs/platform/telemetry/common/telemetryUtils';
import { ExperimentalPrompts } from 'vs/workbench/contrib/experiments/electron-browser/experimentalPrompt';
@@ -59,11 +59,11 @@ suite('Experimental Prompts', () => {
setup(() => {
storageData = {};
instantiationService.stub(IStorageService, {
get: (a, b, c) => a === 'experiments.experiment1' ? JSON.stringify(storageData) : c,
instantiationService.stub(IStorageService, <Partial<IStorageService>>{
get: (a: string, b: StorageScope, c?: string) => a === 'experiments.experiment1' ? JSON.stringify(storageData) : c,
store: (a, b, c) => {
if (a === 'experiments.experiment1') {
storageData = JSON.parse(b);
storageData = JSON.parse(b + '');
}
}
});
@@ -91,7 +91,7 @@ suite('Experimental Prompts', () => {
};
instantiationService.stub(INotificationService, {
prompt: (a: Severity, b: string, c: IPromptChoice[], options) => {
prompt: (a: Severity, b: string, c: IPromptChoice[], options: IPromptOptions) => {
assert.equal(b, promptText);
assert.equal(c.length, 2);
c[0].run();
@@ -116,7 +116,7 @@ suite('Experimental Prompts', () => {
};
instantiationService.stub(INotificationService, {
prompt: (a: Severity, b: string, c: IPromptChoice[], options) => {
prompt: (a: Severity, b: string, c: IPromptChoice[]) => {
assert.equal(b, promptText);
assert.equal(c.length, 2);
c[1].run();
@@ -141,10 +141,10 @@ suite('Experimental Prompts', () => {
};
instantiationService.stub(INotificationService, {
prompt: (a: Severity, b: string, c: IPromptChoice[], options) => {
prompt: (a: Severity, b: string, c: IPromptChoice[], options: IPromptOptions) => {
assert.equal(b, promptText);
assert.equal(c.length, 2);
options.onCancel();
options.onCancel!();
return undefined!;
}
});
@@ -194,4 +194,4 @@ suite('Experimental Prompts', () => {
assert.equal(ExperimentalPrompts.getLocalizedText(englishUSTextCase.promptText, 'fr'), englishUSTextCase.promptText['en-us']);
assert.equal(!!ExperimentalPrompts.getLocalizedText(noEnglishTextCase.promptText, 'fr'), false);
});
});
});

View File

@@ -30,7 +30,7 @@ export class Query {
return [];
}
if (subcommands[command]) {
return subcommands[command].map(subcommand => `@${command}:${subcommand}${subcommand === '' ? '' : ' '}`);
return subcommands[command].map((subcommand: string) => `@${command}:${subcommand}${subcommand === '' ? '' : ' '}`);
}
else {
return [`@${command} `];

View File

@@ -51,7 +51,7 @@ import { KeybindingParser } from 'vs/base/common/keybindingParser';
import { IStorageService } from 'vs/platform/storage/common/storage';
import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
import { getDefaultValue } from 'vs/platform/configuration/common/configurationRegistry';
import { isUndefined } from 'vs/base/common/types';
import { isUndefined, withUndefinedAsNull } from 'vs/base/common/types';
import { IWorkbenchThemeService } from 'vs/workbench/services/themes/common/workbenchThemeService';
// {{SQL CARBON EDIT}}
@@ -258,7 +258,7 @@ export class ExtensionEditor extends BaseEditor {
if (action instanceof ExtensionEditorDropDownAction) {
return action.createActionItem();
}
return null;
return undefined;
}
});
@@ -729,7 +729,7 @@ export class ExtensionEditor extends BaseEditor {
constructor(extension: IExtension, parent?: IExtensionData) {
this.extension = extension;
this.parent = parent || null;
this.parent = withUndefinedAsNull(parent);
}
get hasChildren(): boolean {

View File

@@ -161,12 +161,14 @@ export class ExtensionsListView extends ViewletPanel {
case 'name': options = assign(options, { sortBy: SortBy.Title }); break;
}
const successCallback = model => {
const successCallback = (model: IPagedModel<IExtension>) => {
this.queryRequest = null;
this.setModel(model);
return model;
};
const errorCallback = e => {
const errorCallback = (e: Error) => {
const model = new PagedModel([]);
if (!isPromiseCanceledError(e)) {
this.queryRequest = null;
@@ -534,7 +536,7 @@ export class ExtensionsListView extends ViewletPanel {
return Promise.all([othersPromise, workspacePromise])
.then(([others, workspaceRecommendations]) => {
fileBasedRecommendations = fileBasedRecommendations.filter(x => workspaceRecommendations.every(({ extensionId }) => x.extensionId !== extensionId));
others = others.filter(x => x => workspaceRecommendations.every(({ extensionId }) => x.extensionId !== extensionId));
others = others.filter(x => workspaceRecommendations.every(({ extensionId }) => x.extensionId !== extensionId));
const names = this.getTrimmedRecommendations(local, value, fileBasedRecommendations, others, []);
const recommendationsWithReason = this.tipsService.getAllRecommendationsWithReason();

View File

@@ -12,7 +12,7 @@ import { localize } from 'vs/nls';
import { IExtensionManagementServerService, IExtensionTipsService } from 'vs/platform/extensionManagement/common/extensionManagement';
import { ILabelService } from 'vs/platform/label/common/label';
import { extensionButtonProminentBackground, extensionButtonProminentForeground } from 'vs/workbench/contrib/extensions/electron-browser/extensionsActions';
import { IThemeService } from 'vs/platform/theme/common/themeService';
import { IThemeService, ITheme } from 'vs/platform/theme/common/themeService';
import { STATUS_BAR_HOST_NAME_BACKGROUND, STATUS_BAR_FOREGROUND, STATUS_BAR_NO_FOLDER_FOREGROUND } from 'vs/workbench/common/theme';
import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace';
import { REMOTE_HOST_SCHEME } from 'vs/platform/remote/common/remoteHosts';
@@ -178,7 +178,7 @@ export class RecommendationWidget extends ExtensionWidget {
this.element = append(this.parent, $('div.bookmark'));
const recommendation = append(this.element, $('.recommendation'));
append(recommendation, $('span.octicon.octicon-star'));
const applyBookmarkStyle = (theme) => {
const applyBookmarkStyle = (theme: ITheme) => {
const bgColor = theme.getColor(extensionButtonProminentBackground);
const fgColor = theme.getColor(extensionButtonProminentForeground);
recommendation.style.borderTopColor = bgColor ? bgColor.toString() : 'transparent';

View File

@@ -69,9 +69,9 @@ suite('ExtensionsWorkbenchServiceTest', () => {
instantiationService.stub(ISharedProcessService, TestSharedProcessService);
instantiationService.stub(IWorkspaceContextService, new TestContextService());
instantiationService.stub(IConfigurationService, {
instantiationService.stub(IConfigurationService, <Partial<IConfigurationService>>{
onDidChangeConfiguration: () => { return undefined!; },
getValue: (key?) => {
getValue: (key?: string) => {
return (key === AutoCheckUpdatesConfigurationKey || key === AutoUpdateConfigurationKey) ? true : undefined;
}
});

View File

@@ -27,6 +27,7 @@ import { IEditorService } from 'vs/workbench/services/editor/common/editorServic
import { IEditorGroupsService, IEditorGroup } from 'vs/workbench/services/editor/common/editorGroupsService';
import { ResourceQueue, timeout } from 'vs/base/common/async';
import { onUnexpectedError } from 'vs/base/common/errors';
import { withNullAsUndefined } from 'vs/base/common/types';
// {{SQL CARBON EDIT}}
import { QueryInput } from 'sql/parts/query/common/queryInput';
@@ -284,7 +285,7 @@ export class FileEditorTracker extends Disposable implements IWorkbenchContribut
if (editorResource && resource.toString() === editorResource.toString()) {
const control = editor.getControl();
if (isCodeEditor(control)) {
return control.saveViewState() || undefined;
return withNullAsUndefined(control.saveViewState());
}
}
}

View File

@@ -144,7 +144,7 @@ export class NewFileAction extends BaseErrorReportingAction {
}
const stat = new ExplorerItem(PLACEHOLDER_URI, folder, false);
return folder.fetchChildren(this.fileService).then(() => {
return folder.fetchChildren(this.fileService, this.explorerService).then(() => {
folder.addChild(stat);
const onSuccess = (value: string) => {
@@ -212,7 +212,7 @@ export class NewFolderAction extends BaseErrorReportingAction {
}
const stat = new ExplorerItem(PLACEHOLDER_URI, folder, true);
return folder.fetchChildren(this.fileService).then(() => {
return folder.fetchChildren(this.fileService, this.explorerService).then(() => {
folder.addChild(stat);
const onSuccess = (value: string) => {

View File

@@ -6,7 +6,6 @@
import * as nls from 'vs/nls';
import { URI } from 'vs/base/common/uri';
import * as perf from 'vs/base/common/performance';
import { sequence } from 'vs/base/common/async';
import { Action, IAction } from 'vs/base/common/actions';
import { memoize } from 'vs/base/common/decorators';
import { IFilesConfiguration, ExplorerFolderContext, FilesExplorerFocusedContext, ExplorerFocusedContext, ExplorerRootContext, ExplorerResourceReadonlyContext, IExplorerService, ExplorerResourceCut } from 'vs/workbench/contrib/files/common/files';
@@ -49,6 +48,9 @@ import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService
import { isMacintosh } from 'vs/base/common/platform';
import { KeyCode } from 'vs/base/common/keyCodes';
import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent';
import { isEqualOrParent } from 'vs/base/common/resources';
import { values } from 'vs/base/common/map';
import { first } from 'vs/base/common/arrays';
import { withNullAsUndefined } from 'vs/base/common/types';
export class ExplorerView extends ViewletPanel {
@@ -188,7 +190,7 @@ export class ExplorerView extends ViewletPanel {
this.tree.domFocus();
}
}));
this.disposables.push(this.explorerService.onDidSelectItem(e => this.onSelectItem(e.item, e.reveal)));
this.disposables.push(this.explorerService.onDidSelectResource(e => this.onSelectResource(e.resource, e.reveal)));
this.disposables.push(this.explorerService.onDidCopyItems(e => this.onCopyItems(e.items, e.cut, e.previouslyCutItems)));
// Update configuration
@@ -507,27 +509,27 @@ export class ExplorerView extends ViewletPanel {
return withNullAsUndefined(toResource(input, { supportSideBySide: true }));
}
private onSelectItem(fileStat: ExplorerItem | undefined, reveal = this.autoReveal): Promise<void> {
if (!fileStat || !this.isBodyVisible() || this.tree.getInput() === fileStat) {
return Promise.resolve(undefined);
private async onSelectResource(resource: URI | undefined, reveal = this.autoReveal): Promise<void> {
if (!resource || !this.isBodyVisible()) {
return;
}
// Expand all stats in the parent chain
const toExpand: ExplorerItem[] = [];
let parent = fileStat.parent;
while (parent) {
toExpand.push(parent);
parent = parent.parent;
let item: ExplorerItem | undefined = this.explorerService.roots.filter(i => isEqualOrParent(resource, i.resource))[0];
while (item && item.resource.toString() !== resource.toString()) {
await this.tree.expand(item);
item = first(values(item.children), i => isEqualOrParent(resource, i.resource));
}
return sequence(toExpand.reverse().map(s => () => this.tree.expand(s))).then(() => {
if (item && item.parent) {
if (reveal) {
this.tree.reveal(fileStat, 0.5);
this.tree.reveal(item, 0.5);
}
this.tree.setFocus([fileStat]);
this.tree.setSelection([fileStat]);
});
this.tree.setFocus([item]);
this.tree.setSelection([item]);
}
}
private onCopyItems(stats: ExplorerItem[], cut: boolean, previousCut: ExplorerItem[] | undefined): void {

View File

@@ -9,7 +9,7 @@ import * as glob from 'vs/base/common/glob';
import { IListVirtualDelegate, ListDragOverEffect } from 'vs/base/browser/ui/list/list';
import { IProgressService } from 'vs/platform/progress/common/progress';
import { INotificationService } from 'vs/platform/notification/common/notification';
import { IFileService, FileKind, IFileStat, FileOperationError, FileOperationResult } from 'vs/platform/files/common/files';
import { IFileService, FileKind, FileOperationError, FileOperationResult } from 'vs/platform/files/common/files';
import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService';
import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace';
import { IDisposable, Disposable, dispose, toDisposable } from 'vs/base/common/lifecycle';
@@ -63,10 +63,11 @@ export class ExplorerDelegate implements IListVirtualDelegate<ExplorerItem> {
export class ExplorerDataSource implements IAsyncDataSource<ExplorerItem | ExplorerItem[], ExplorerItem> {
constructor(
@IProgressService private progressService: IProgressService,
@INotificationService private notificationService: INotificationService,
@IWorkbenchLayoutService private layoutService: IWorkbenchLayoutService,
@IFileService private fileService: IFileService
@IProgressService private readonly progressService: IProgressService,
@INotificationService private readonly notificationService: INotificationService,
@IWorkbenchLayoutService private readonly layoutService: IWorkbenchLayoutService,
@IFileService private readonly fileService: IFileService,
@IExplorerService private readonly explorerService: IExplorerService
) { }
hasChildren(element: ExplorerItem | ExplorerItem[]): boolean {
@@ -78,7 +79,7 @@ export class ExplorerDataSource implements IAsyncDataSource<ExplorerItem | Explo
return Promise.resolve(element);
}
const promise = element.fetchChildren(this.fileService).then(undefined, e => {
const promise = element.fetchChildren(this.fileService, this.explorerService).then(undefined, e => {
// Do not show error for roots since we already use an explorer decoration to notify user
if (!(element instanceof ExplorerItem && element.isRoot)) {
this.notificationService.error(e);
@@ -635,12 +636,12 @@ export class FileDragAndDrop implements ITreeDragAndDrop<ExplorerItem> {
if (resources && resources.length > 0) {
// Resolve target to check for name collisions and ask user
return this.fileService.resolveFile(target.resource).then((targetStat: IFileStat) => {
return this.fileService.resolveFile(target.resource).then(targetStat => {
// Check for name collisions
const targetNames = new Set<string>();
if (targetStat.children) {
targetStat.children.forEach((child) => {
targetStat.children.forEach(child => {
targetNames.add(isLinux ? child.name : child.name.toLowerCase());
});
}

View File

@@ -16,6 +16,7 @@ import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import { memoize } from 'vs/base/common/decorators';
import { Emitter, Event } from 'vs/base/common/event';
import { IExplorerService } from 'vs/workbench/contrib/files/common/files';
export class ExplorerModel implements IDisposable {
@@ -85,7 +86,6 @@ export class ExplorerItem {
private _isReadonly?: boolean,
private _name: string = resources.basenameOrAuthority(resource),
private _mtime?: number,
private _etag?: string,
) {
this._isDirectoryResolved = false;
}
@@ -106,10 +106,6 @@ export class ExplorerItem {
return !!this._isReadonly;
}
get etag(): string | undefined {
return this._etag;
}
get mtime(): number | undefined {
return this._mtime;
}
@@ -154,7 +150,7 @@ export class ExplorerItem {
}
static create(raw: IFileStat, parent: ExplorerItem | undefined, resolveTo?: URI[]): ExplorerItem {
const stat = new ExplorerItem(raw.resource, parent, raw.isDirectory, raw.isSymbolicLink, raw.isReadonly, raw.name, raw.mtime, raw.etag);
const stat = new ExplorerItem(raw.resource, parent, raw.isDirectory, raw.isSymbolicLink, raw.isReadonly, raw.name, raw.mtime);
// Recursively add children if present
if (stat.isDirectory) {
@@ -247,10 +243,13 @@ export class ExplorerItem {
return this.children.get(this.getPlatformAwareName(name));
}
fetchChildren(fileService: IFileService): Promise<ExplorerItem[]> {
fetchChildren(fileService: IFileService, explorerService: IExplorerService): Promise<ExplorerItem[]> {
let promise: Promise<any> = Promise.resolve(undefined);
if (!this._isDirectoryResolved) {
promise = fileService.resolveFile(this.resource, { resolveSingleChildDescendants: true }).then(stat => {
// Resolve metadata only when the mtime is needed since this can be expensive
// Mtime is only used when the sort order is 'modified'
const resolveMetadata = explorerService.sortOrder === 'modified';
promise = fileService.resolveFile(this.resource, { resolveSingleChildDescendants: true, resolveMetadata }).then(stat => {
const resolved = ExplorerItem.create(stat, this);
ExplorerItem.mergeLocalWithDisk(resolved, this);
this._isDirectoryResolved = true;

View File

@@ -34,7 +34,7 @@ export class ExplorerService implements IExplorerService {
private _onDidChangeRoots = new Emitter<void>();
private _onDidChangeItem = new Emitter<ExplorerItem | undefined>();
private _onDidChangeEditable = new Emitter<ExplorerItem>();
private _onDidSelectItem = new Emitter<{ item?: ExplorerItem, reveal?: boolean }>();
private _onDidSelectResource = new Emitter<{ resource?: URI, reveal?: boolean }>();
private _onDidCopyItems = new Emitter<{ items: ExplorerItem[], cut: boolean, previouslyCutItems: ExplorerItem[] | undefined }>();
private disposables: IDisposable[] = [];
private editable: { stat: ExplorerItem, data: IEditableData } | undefined;
@@ -68,8 +68,8 @@ export class ExplorerService implements IExplorerService {
return this._onDidChangeEditable.event;
}
get onDidSelectItem(): Event<{ item?: ExplorerItem, reveal?: boolean }> {
return this._onDidSelectItem.event;
get onDidSelectResource(): Event<{ resource?: URI, reveal?: boolean }> {
return this._onDidSelectResource.event;
}
get onDidCopyItems(): Event<{ items: ExplorerItem[], cut: boolean, previouslyCutItems: ExplorerItem[] | undefined }> {
@@ -142,12 +142,12 @@ export class ExplorerService implements IExplorerService {
select(resource: URI, reveal?: boolean): Promise<void> {
const fileStat = this.findClosest(resource);
if (fileStat) {
this._onDidSelectItem.fire({ item: fileStat, reveal });
this._onDidSelectResource.fire({ resource: fileStat.resource, reveal });
return Promise.resolve(undefined);
}
// Stat needs to be resolved first and then revealed
const options: IResolveFileOptions = { resolveTo: [resource] };
const options: IResolveFileOptions = { resolveTo: [resource], resolveMetadata: false };
const workspaceFolder = this.contextService.getWorkspaceFolder(resource);
const rootUri = workspaceFolder ? workspaceFolder.uri : this.roots[0].resource;
const root = this.roots.filter(r => r.resource.toString() === rootUri.toString()).pop()!;
@@ -161,7 +161,7 @@ export class ExplorerService implements IExplorerService {
this._onDidChangeItem.fire(item ? item.parent : undefined);
// Select and Reveal
this._onDidSelectItem.fire({ item: item || undefined, reveal });
this._onDidSelectResource.fire({ resource: item ? item.resource : undefined, reveal });
}, () => {
root.isError = true;
this._onDidChangeItem.fire(root);
@@ -192,7 +192,8 @@ export class ExplorerService implements IExplorerService {
// Add the new file to its parent (Model)
parents.forEach(p => {
// We have to check if the parent is resolved #29177
const thenable: Promise<IFileStat | undefined> = p.isDirectoryResolved ? Promise.resolve(undefined) : this.fileService.resolveFile(p.resource);
const resolveMetadata = this.sortOrder === `modified`;
const thenable: Promise<IFileStat | undefined> = p.isDirectoryResolved ? Promise.resolve(undefined) : this.fileService.resolveFile(p.resource, { resolveMetadata });
thenable.then(stat => {
if (stat) {
const modelStat = ExplorerItem.create(stat, p.parent);
@@ -357,10 +358,10 @@ export class ExplorerService implements IExplorerService {
private onConfigurationUpdated(configuration: IFilesConfiguration, event?: IConfigurationChangeEvent): void {
const configSortOrder = configuration && configuration.explorer && configuration.explorer.sortOrder || 'default';
if (this._sortOrder !== configSortOrder) {
const shouldFire = this._sortOrder !== undefined;
const shouldRefresh = this._sortOrder !== undefined;
this._sortOrder = configSortOrder;
if (shouldFire) {
this._onDidChangeRoots.fire();
if (shouldRefresh) {
this.refresh();
}
}
}

View File

@@ -44,7 +44,7 @@ export interface IExplorerService {
readonly onDidChangeRoots: Event<void>;
readonly onDidChangeItem: Event<ExplorerItem | undefined>;
readonly onDidChangeEditable: Event<ExplorerItem>;
readonly onDidSelectItem: Event<{ item?: ExplorerItem, reveal?: boolean }>;
readonly onDidSelectResource: Event<{ resource?: URI, reveal?: boolean }>;
readonly onDidCopyItems: Event<{ items: ExplorerItem[], cut: boolean, previouslyCutItems: ExplorerItem[] | undefined }>;
setEditable(stat: ExplorerItem, data: IEditableData | null): void;

View File

@@ -147,7 +147,6 @@ suite('Files - View Model', function () {
assert.strictEqual(s1.find(toResource.call(this, 'foobar')), null);
assert.strictEqual(s1.find(toResource.call(this, '/')), s1);
// assert.strictEqual(s1.find(toResource.call(this, '')), s1); //TODO@isidor this fails with proper paths usage
});
test('Find with mixed case', function () {
@@ -244,21 +243,19 @@ suite('Files - View Model', function () {
});
test('Merge Local with Disk', function () {
const d = new Date().toUTCString();
const merge1 = new ExplorerItem(URI.file(join('C:\\', '/path/to')), undefined, true, false, false, 'to', Date.now(), d);
const merge2 = new ExplorerItem(URI.file(join('C:\\', '/path/to')), undefined, true, false, false, 'to', Date.now(), new Date(0).toUTCString());
const merge1 = new ExplorerItem(URI.file(join('C:\\', '/path/to')), undefined, true, false, false, 'to', Date.now());
const merge2 = new ExplorerItem(URI.file(join('C:\\', '/path/to')), undefined, true, false, false, 'to', Date.now());
// Merge Properties
ExplorerItem.mergeLocalWithDisk(merge2, merge1);
assert.strictEqual(merge1.mtime, merge2.mtime);
// Merge Child when isDirectoryResolved=false is a no-op
merge2.addChild(new ExplorerItem(URI.file(join('C:\\', '/path/to/foo.html')), undefined, true, false, false, 'foo.html', Date.now(), d));
merge2.addChild(new ExplorerItem(URI.file(join('C:\\', '/path/to/foo.html')), undefined, true, false, false, 'foo.html', Date.now()));
ExplorerItem.mergeLocalWithDisk(merge2, merge1);
// Merge Child with isDirectoryResolved=true
const child = new ExplorerItem(URI.file(join('C:\\', '/path/to/foo.html')), undefined, true, false, false, 'foo.html', Date.now(), d);
const child = new ExplorerItem(URI.file(join('C:\\', '/path/to/foo.html')), undefined, true, false, false, 'foo.html', Date.now());
merge2.removeChild(child);
merge2.addChild(child);
(<any>merge2)._isDirectoryResolved = true;

View File

@@ -3,57 +3,6 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle';
import { Registry } from 'vs/platform/registry/common/platform';
import { Extensions, IWorkbenchContributionsRegistry } from 'vs/workbench/common/contributions';
import { INotificationService, Severity } from 'vs/platform/notification/common/notification';
import { setFormatterConflictCallback, FormatMode, FormatKind } from 'vs/editor/contrib/format/format';
import { IDisposable } from 'vs/base/common/lifecycle';
import { localize } from 'vs/nls';
import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet';
import { VIEWLET_ID, IExtensionsViewlet } from 'vs/workbench/contrib/extensions/common/extensions';
class FormattingConflictHandler {
private _registration: IDisposable;
constructor(
@INotificationService notificationService: INotificationService,
@IViewletService private readonly _viewletService: IViewletService,
) {
this._registration = setFormatterConflictCallback((ids, model, mode) => {
if (mode & FormatMode.Auto) {
return;
}
if (ids.length === 0) {
const langName = model.getLanguageIdentifier().language;
const message = mode & FormatKind.Document
? localize('no.documentprovider', "There is no document formatter for '{0}'-files installed.", langName)
: localize('no.selectionprovider', "There is no selection formatter for '{0}'-files installed.", langName);
const choice = {
label: localize('install.formatter', "Install Formatter..."),
run: () => {
return this._viewletService.openViewlet(VIEWLET_ID, true).then(viewlet => {
if (viewlet) {
(viewlet as IExtensionsViewlet).search(`category:formatters ${langName}`);
}
});
}
};
notificationService.prompt(Severity.Info, message, [choice]);
}
});
}
dispose(): void {
this._registration.dispose();
}
}
Registry.as<IWorkbenchContributionsRegistry>(Extensions.Workbench).registerWorkbenchContribution(
FormattingConflictHandler,
LifecyclePhase.Restored
);
import 'vs/css!./media/format.contribution';
import './formatActionsMultiple';
import './formatActionsNone';

View File

@@ -0,0 +1,138 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { KeyChord, KeyCode, KeyMod } from 'vs/base/common/keyCodes';
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
import { EditorAction, registerEditorAction, ServicesAccessor } from 'vs/editor/browser/editorExtensions';
import { EditorContextKeys } from 'vs/editor/common/editorContextKeys';
import { DocumentRangeFormattingEditProviderRegistry } from 'vs/editor/common/modes';
import * as nls from 'vs/nls';
import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry';
import { IQuickInputService, IQuickPickItem, IQuickInputButton } from 'vs/platform/quickinput/common/quickInput';
import { CancellationToken } from 'vs/base/common/cancellation';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { formatDocumentRangeWithProvider, formatDocumentWithProvider, getRealAndSyntheticDocumentFormattersOrdered } from 'vs/editor/contrib/format/format';
import { Range } from 'vs/editor/common/core/range';
import { showExtensionQuery } from 'vs/workbench/contrib/format/browser/showExtensionQuery';
import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet';
interface IIndexedPick extends IQuickPickItem {
index: number;
}
const openExtensionAction: IQuickInputButton = {
tooltip: nls.localize('show.ext', "Show extension..."),
iconClass: 'format-show-extension'
};
registerEditorAction(class FormatDocumentMultipleAction extends EditorAction {
constructor() {
super({
id: 'editor.action.formatDocument.multiple',
label: nls.localize('formatDocument.label.multiple', "Format Document..."),
alias: 'Format Document...',
precondition: ContextKeyExpr.and(EditorContextKeys.writable, EditorContextKeys.hasMultipleDocumentFormattingProvider),
kbOpts: {
kbExpr: EditorContextKeys.editorTextFocus,
primary: KeyMod.Shift | KeyMod.Alt | KeyCode.KEY_F,
linux: { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_I },
weight: KeybindingWeight.EditorContrib,
},
menuOpts: {
group: '1_modification',
order: 1.3
}
});
}
async run(accessor: ServicesAccessor, editor: ICodeEditor, args: any): Promise<void> {
if (!editor.hasModel()) {
return;
}
const instaService = accessor.get(IInstantiationService);
const quickPickService = accessor.get(IQuickInputService);
const viewletService = accessor.get(IViewletService);
const model = editor.getModel();
const provider = getRealAndSyntheticDocumentFormattersOrdered(model);
const picks = provider.map((provider, index) => {
return <IIndexedPick>{
index,
label: provider.displayName || '',
buttons: [openExtensionAction]
};
});
const pick = await quickPickService.pick(picks, {
placeHolder: nls.localize('format.placeHolder', "Select a formatter"),
onDidTriggerItemButton: (e) => {
const { extensionId } = provider[e.item.index];
return showExtensionQuery(viewletService, `@id:${extensionId!.value}`);
}
});
if (pick) {
await instaService.invokeFunction(formatDocumentWithProvider, provider[pick.index], editor, CancellationToken.None);
}
}
});
registerEditorAction(class FormatSelectionMultipleAction extends EditorAction {
constructor() {
super({
id: 'editor.action.formatSelection.multiple',
label: nls.localize('formatSelection.label.multiple', "Format Selection..."),
alias: 'Format Code...',
precondition: ContextKeyExpr.and(ContextKeyExpr.and(EditorContextKeys.writable), EditorContextKeys.hasMultipleDocumentSelectionFormattingProvider),
kbOpts: {
kbExpr: EditorContextKeys.editorTextFocus,
primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.KEY_F),
weight: KeybindingWeight.EditorContrib
},
menuOpts: {
when: ContextKeyExpr.and(EditorContextKeys.hasNonEmptySelection),
group: '1_modification',
order: 1.31
}
});
}
async run(accessor: ServicesAccessor, editor: ICodeEditor): Promise<void> {
if (!editor.hasModel()) {
return;
}
const instaService = accessor.get(IInstantiationService);
const quickPickService = accessor.get(IQuickInputService);
const viewletService = accessor.get(IViewletService);
const model = editor.getModel();
let range: Range = editor.getSelection();
if (range.isEmpty()) {
range = new Range(range.startLineNumber, 1, range.startLineNumber, model.getLineMaxColumn(range.startLineNumber));
}
const provider = DocumentRangeFormattingEditProviderRegistry.ordered(model);
const picks = provider.map((provider, index) => {
return <IIndexedPick>{
index,
label: provider.displayName || '',
buttons: [openExtensionAction]
};
});
const pick = await quickPickService.pick(picks, {
placeHolder: nls.localize('format.placeHolder', "Select a formatter"),
onDidTriggerItemButton: (e) => {
const { extensionId } = provider[e.item.index];
return showExtensionQuery(viewletService, `@id:${extensionId!.value}`);
}
});
if (pick) {
await instaService.invokeFunction(formatDocumentRangeWithProvider, provider[pick.index], editor, range, CancellationToken.None);
}
}
});

View File

@@ -0,0 +1,61 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { KeyCode, KeyMod } from 'vs/base/common/keyCodes';
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
import { EditorAction, registerEditorAction, ServicesAccessor } from 'vs/editor/browser/editorExtensions';
import { EditorContextKeys } from 'vs/editor/common/editorContextKeys';
import { DocumentFormattingEditProviderRegistry } from 'vs/editor/common/modes';
import * as nls from 'vs/nls';
import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry';
import { ICommandService } from 'vs/platform/commands/common/commands';
import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet';
import { INotificationService, Severity } from 'vs/platform/notification/common/notification';
import { showExtensionQuery } from 'vs/workbench/contrib/format/browser/showExtensionQuery';
registerEditorAction(class FormatDocumentMultipleAction extends EditorAction {
constructor() {
super({
id: 'editor.action.formatDocument.none',
label: nls.localize('formatDocument.label.multiple', "Format Document"),
alias: 'Format Document',
precondition: ContextKeyExpr.and(EditorContextKeys.writable, EditorContextKeys.hasDocumentFormattingProvider.toNegated()),
kbOpts: {
kbExpr: ContextKeyExpr.and(EditorContextKeys.editorTextFocus, EditorContextKeys.hasDocumentFormattingProvider.toNegated()),
primary: KeyMod.Shift | KeyMod.Alt | KeyCode.KEY_F,
linux: { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_I },
weight: KeybindingWeight.EditorContrib,
}
});
}
async run(accessor: ServicesAccessor, editor: ICodeEditor, args: any): Promise<void> {
if (!editor.hasModel()) {
return;
}
const commandService = accessor.get(ICommandService);
const viewletService = accessor.get(IViewletService);
const notificationService = accessor.get(INotificationService);
const model = editor.getModel();
const formatterCount = DocumentFormattingEditProviderRegistry.all(model).length;
if (formatterCount > 1) {
return commandService.executeCommand('editor.action.formatDocument.multiple');
} else if (formatterCount === 1) {
return commandService.executeCommand('editor.action.formatDocument');
} else {
const langName = model.getLanguageIdentifier().language;
const message = nls.localize('no.rovider', "There is no formatter for '{0}'-files installed.", langName);
const choice = {
label: nls.localize('install.formatter', "Install Formatter..."),
run: () => showExtensionQuery(viewletService, `category:formatters ${langName}`)
};
notificationService.prompt(Severity.Info, message, [choice]);
}
}
});

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"><g fill="#C5C5C5"><path d="M12.714 9.603c-.07.207-.15.407-.246.601l1.017 2.139c-.335.424-.718.807-1.142 1.143l-2.14-1.018c-.193.097-.394.176-.601.247l-.795 2.235c-.265.03-.534.05-.807.05-.272 0-.541-.02-.806-.05l-.795-2.235c-.207-.071-.408-.15-.602-.247l-2.14 1.017c-.424-.336-.807-.719-1.143-1.143l1.017-2.139c-.094-.193-.175-.393-.245-.6l-2.236-.796c-.03-.265-.05-.534-.05-.807s.02-.542.05-.807l2.236-.795c.07-.207.15-.407.246-.601l-1.016-2.139c.336-.423.719-.807 1.143-1.142l2.14 1.017c.193-.096.394-.176.602-.247l.793-2.236c.265-.03.534-.05.806-.05.273 0 .542.02.808.05l.795 2.236c.207.07.407.15.601.246l2.14-1.017c.424.335.807.719 1.142 1.142l-1.017 2.139c.096.194.176.394.246.601l2.236.795c.029.266.049.535.049.808s-.02.542-.05.807l-2.236.796zm-4.714-4.603c-1.657 0-3 1.343-3 3s1.343 3 3 3 3-1.343 3-3-1.343-3-3-3z"/><circle cx="8" cy="8" r="1.5"/></g></svg>

After

Width:  |  Height:  |  Size: 927 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"><g fill="#424242"><path d="M12.714 9.603c-.07.207-.15.407-.246.601l1.017 2.139c-.335.424-.718.807-1.142 1.143l-2.14-1.018c-.193.097-.394.176-.601.247l-.795 2.235c-.265.03-.534.05-.807.05-.272 0-.541-.02-.806-.05l-.795-2.235c-.207-.071-.408-.15-.602-.247l-2.14 1.017c-.424-.336-.807-.719-1.143-1.143l1.017-2.139c-.094-.193-.175-.393-.245-.6l-2.236-.796c-.03-.265-.05-.534-.05-.807s.02-.542.05-.807l2.236-.795c.07-.207.15-.407.246-.601l-1.016-2.139c.336-.423.719-.807 1.143-1.142l2.14 1.017c.193-.096.394-.176.602-.247l.793-2.236c.265-.03.534-.05.806-.05.273 0 .542.02.808.05l.795 2.236c.207.07.407.15.601.246l2.14-1.017c.424.335.807.719 1.142 1.142l-1.017 2.139c.096.194.176.394.246.601l2.236.795c.029.266.049.535.049.808s-.02.542-.05.807l-2.236.796zm-4.714-4.603c-1.657 0-3 1.343-3 3s1.343 3 3 3 3-1.343 3-3-1.343-3-3-3z"/><circle cx="8" cy="8" r="1.5"/></g></svg>

After

Width:  |  Height:  |  Size: 927 B

View File

@@ -0,0 +1,13 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
.monaco-workbench .format-show-extension {
background-image: url('configure.svg');
}
.vs-dark .monaco-workbench .format-show-extension,
.hc-black .monaco-workbench .format-show-extension {
background-image: url('configure-inverse.svg');
}

View File

@@ -0,0 +1,15 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet';
import { VIEWLET_ID, IExtensionsViewlet } from 'vs/workbench/contrib/extensions/common/extensions';
export function showExtensionQuery(viewletService: IViewletService, query: string) {
return viewletService.openViewlet(VIEWLET_ID, true).then(viewlet => {
if (viewlet) {
(viewlet as IExtensionsViewlet).search(query);
}
});
}

View File

@@ -38,7 +38,7 @@ class LogOutputChannels extends Disposable implements IWorkbenchContribution {
outputChannelRegistry.registerChannel({ id: Constants.sqlToolsLogChannellId, label: nls.localize('sqlToolsLog', "Log (SqlTools)"), file: URI.file(toolsServiceLogFile), log: true });
// {{SQL CARBON EDIT}} - End
const registerTelemetryChannel = (level) => {
const registerTelemetryChannel = (level: LogLevel) => {
if (level === LogLevel.Trace && !outputChannelRegistry.getChannel(Constants.telemetryLogChannelId)) {
outputChannelRegistry.registerChannel({ id: Constants.telemetryLogChannelId, label: nls.localize('telemetryLog', "Telemetry"), file: URI.file(join(environmentService.logsPath, `telemetry.log`)), log: true });
}

View File

@@ -12,6 +12,7 @@ import { values } from 'vs/base/common/map';
import { memoize } from 'vs/base/common/decorators';
import { Emitter, Event } from 'vs/base/common/event';
import { Hasher } from 'vs/base/common/hash';
import { withUndefinedAsNull } from 'vs/base/common/types';
function compareUris(a: URI, b: URI) {
const astr = a.toString();
@@ -138,7 +139,7 @@ export class MarkersModel {
}
getResourceMarkers(resource: URI): ResourceMarkers | null {
return this.resourcesByUri.get(resource.toString()) || null;
return withUndefinedAsNull(this.resourcesByUri.get(resource.toString()));
}
setResourceMarkers(resource: URI, rawMarkers: IMarker[]): void {

View File

@@ -628,7 +628,7 @@ export class MarkersPanel extends Panel implements IMarkerFilterController {
if (keybinding) {
return new ActionItem(action, action, { label: true, keybinding: keybinding.getLabel() });
}
return null;
return undefined;
},
onHide: (wasCancelled?: boolean) => {
if (wasCancelled) {
@@ -670,7 +670,7 @@ export class MarkersPanel extends Panel implements IMarkerFilterController {
return this.tree.getFocus()[0];
}
public getActionItem(action: IAction): IActionItem | null {
public getActionItem(action: IAction): IActionItem | undefined {
if (action.id === MarkersFilterAction.ID) {
this.filterInputActionItem = this.instantiationService.createInstance(MarkersFilterActionItem, this.filterAction, this);
return this.filterInputActionItem;

View File

@@ -253,7 +253,7 @@ class MarkerWidget extends Disposable {
) {
super();
this.actionBar = this._register(new ActionBar(dom.append(parent, dom.$('.actions')), {
actionItemProvider: (action) => action.id === QuickFixAction.ID ? instantiationService.createInstance(QuickFixActionItem, action) : null
actionItemProvider: (action) => action.id === QuickFixAction.ID ? instantiationService.createInstance(QuickFixActionItem, action) : undefined
}));
this.icon = dom.append(parent, dom.$('.icon'));
this.multilineActionbar = this._register(new ActionBar(dom.append(parent, dom.$('.multiline-actions'))));

View File

@@ -75,7 +75,7 @@ export class OutputPanel extends AbstractTextResourceEditor {
return this.actions;
}
public getActionItem(action: Action): IActionItem | null {
public getActionItem(action: Action): IActionItem | undefined {
if (action.id === SwitchOutputAction.ID) {
return this.instantiationService.createInstance(SwitchOutputActionItem, action);
}

View File

@@ -66,7 +66,7 @@ export class OutputService extends Disposable implements IOutputService, ITextMo
private channels: Map<string, OutputChannel> = new Map<string, OutputChannel>();
private activeChannelIdInStorage: string;
private activeChannel: OutputChannel | null;
private activeChannel?: OutputChannel;
private readonly _onActiveOutputChannel = new Emitter<string>();
readonly onActiveOutputChannel: Event<string> = this._onActiveOutputChannel.event;
@@ -105,7 +105,7 @@ export class OutputService extends Disposable implements IOutputService, ITextMo
// Set active channel to first channel if not set
if (!this.activeChannel) {
const channels = this.getChannelDescriptors();
this.activeChannel = channels && channels.length > 0 ? this.getChannel(channels[0].id) : null;
this.activeChannel = channels && channels.length > 0 ? this.getChannel(channels[0].id) : undefined;
}
this._register(this.lifecycleService.onShutdown(() => this.dispose()));
@@ -140,15 +140,15 @@ export class OutputService extends Disposable implements IOutputService, ITextMo
return promise.then(() => this._onActiveOutputChannel.fire(id));
}
getChannel(id: string): OutputChannel | null {
return this.channels.get(id) || null;
getChannel(id: string): OutputChannel | undefined {
return this.channels.get(id);
}
getChannelDescriptors(): IOutputChannelDescriptor[] {
return Registry.as<IOutputChannelRegistry>(Extensions.OutputChannels).getChannels();
}
getActiveChannel(): IOutputChannel | null {
getActiveChannel(): IOutputChannel | undefined {
return this.activeChannel;
}
@@ -194,7 +194,7 @@ export class OutputService extends Disposable implements IOutputService, ITextMo
channel.model.onDispose(() => {
if (this.activeChannel === channel) {
const channels = this.getChannelDescriptors();
const channel = channels.length ? this.getChannel(channels[0].id) : null;
const channel = channels.length ? this.getChannel(channels[0].id) : undefined;
if (channel && this.isPanelShown()) {
this.showChannel(channel.id, true);
} else {

View File

@@ -68,7 +68,7 @@ export interface IOutputService {
* Given the channel id returns the output channel instance.
* Channel should be first registered via OutputChannelRegistry.
*/
getChannel(id: string): IOutputChannel | null;
getChannel(id: string): IOutputChannel | undefined;
/**
* Returns an array of all known output channels descriptors.
@@ -79,7 +79,7 @@ export interface IOutputService {
* Returns the currently active channel.
* Only one channel can be active at a given moment.
*/
getActiveChannel(): IOutputChannel | null;
getActiveChannel(): IOutputChannel | undefined;
/**
* Show the channel with the passed id.

View File

@@ -376,7 +376,7 @@ export class KeybindingsEditor extends BaseEditor implements IKeybindingsEditor
if (action.id === this.recordKeysAction.id) {
return new CheckboxActionItem(null, action);
}
return null;
return undefined;
}
}));

View File

@@ -424,7 +424,7 @@ export class FolderSettingsActionItem extends BaseActionItem {
this.contextMenuService.showContextMenu({
getAnchor: () => this.container,
getActions: () => this.getDropdownMenuActions(),
getActionItem: () => null,
getActionItem: () => undefined,
onHide: () => {
this.anchorElement.blur();
}
@@ -495,7 +495,7 @@ export class SettingsTargetsWidget extends Widget {
orientation: ActionsOrientation.HORIZONTAL,
ariaLabel: localize('settingsSwitcherBarAriaLabel', "Settings Switcher"),
animated: false,
actionItemProvider: (action: Action) => action.id === 'folderSettings' ? this.folderSettings : null
actionItemProvider: (action: Action) => action.id === 'folderSettings' ? this.folderSettings : undefined
}));
this.userSettings = new Action('userSettings', localize('userSettings', "User Settings"), '.settings-tab', true, () => this.updateTarget(ConfigurationTarget.USER));

View File

@@ -439,7 +439,7 @@ export abstract class AbstractSettingRenderer implements ITreeRenderer<SettingsT
}
}
const onChange = value => this._onDidChangeSetting.fire({ key: element.setting.key, value, type: template.context!.valueType });
const onChange = (value: any) => this._onDidChangeSetting.fire({ key: element.setting.key, value, type: template.context!.valueType });
template.deprecationWarningElement.innerText = element.setting.deprecationMessage || '';
this.renderValue(element, <ISettingItemTemplate>template, onChange);
@@ -698,7 +698,7 @@ export class SettingExcludeRenderer extends AbstractSettingRenderer implements I
}
}
const sortKeys = (obj) => {
const sortKeys = (obj: Object) => {
const keyArray = Object.keys(obj)
.map(key => ({ key, val: obj[key] }))
.sort((a, b) => a.key.localeCompare(b.key));
@@ -901,7 +901,7 @@ export class SettingNumberRenderer extends AbstractSettingRenderer implements IT
? parseInt : parseFloat;
const nullNumParseFn = (dataElement.valueType === 'nullable-integer' || dataElement.valueType === 'nullable-number')
? (v => v === '' ? null : numParseFn(v)) : numParseFn;
? ((v: string) => v === '' ? null : numParseFn(v)) : numParseFn;
const label = this.setElementAriaLabels(dataElement, SETTINGS_NUMBER_TEMPLATE_ID, template);

View File

@@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/
import * as arrays from 'vs/base/common/arrays';
import { isArray } from 'vs/base/common/types';
import { isArray, withUndefinedAsNull } from 'vs/base/common/types';
import { URI } from 'vs/base/common/uri';
import { localize } from 'vs/nls';
import { ConfigurationTarget, IConfigurationService } from 'vs/platform/configuration/common/configuration';
@@ -262,11 +262,11 @@ export class SettingsTreeModel {
}
getElementById(id: string): SettingsTreeElement | null {
return this._treeElementsById.get(id) || null;
return withUndefinedAsNull(this._treeElementsById.get(id));
}
getElementsByName(name: string): SettingsTreeSettingElement[] | null {
return this._treeElementsBySettingName.get(name) || null;
return withUndefinedAsNull(this._treeElementsBySettingName.get(name));
}
updateElementsByName(name: string): void {
@@ -378,7 +378,7 @@ function wordifyKey(key: string): string {
}
function trimCategoryForGroup(category: string, groupId: string): string {
const doTrim = forward => {
const doTrim = (forward: boolean) => {
const parts = groupId.split('.');
while (parts.length) {
const reg = new RegExp(`^${parts.join('\\.')}(\\.|$)`, 'i');

View File

@@ -403,7 +403,7 @@ export class ExcludeSettingWidget extends Disposable {
private renderEditItem(item: IExcludeViewItem): HTMLElement {
const rowElement = $('.setting-exclude-edit-row');
const onSubmit = edited => {
const onSubmit = (edited: boolean) => {
this.model.setEditKey(null);
const pattern = patternInput.value.trim();
if (edited && pattern) {

View File

@@ -88,7 +88,7 @@ interface ISerializedPreferencesEditorInput {
// Register Preferences Editor Input Factory
class PreferencesEditorInputFactory implements IEditorInputFactory {
serialize(editorInput: EditorInput): string | null {
serialize(editorInput: EditorInput): string | undefined {
const input = <PreferencesEditorInput>editorInput;
if (input.details && input.master) {
@@ -113,10 +113,10 @@ class PreferencesEditorInputFactory implements IEditorInputFactory {
}
}
return null;
return undefined;
}
deserialize(instantiationService: IInstantiationService, serializedEditorInput: string): EditorInput | null {
deserialize(instantiationService: IInstantiationService, serializedEditorInput: string): EditorInput | undefined {
const deserialized: ISerializedPreferencesEditorInput = JSON.parse(serializedEditorInput);
const registry = Registry.as<IEditorInputFactoryRegistry>(EditorInputExtensions.EditorInputFactories);
@@ -132,7 +132,7 @@ class PreferencesEditorInputFactory implements IEditorInputFactory {
}
}
return null;
return undefined;
}
}

View File

@@ -287,7 +287,7 @@ class RemoteSearchProvider implements ISearchProvider {
const timestamp = Date.now();
const duration = timestamp - start;
const remoteSettings: IRemoteSetting[] = (result.value || [])
.map(r => {
.map((r: any) => {
const key = JSON.parse(r.setting || r.Setting);
const packageId = r['packageid'];
const id = getSettingKey(key, packageId);
@@ -447,7 +447,7 @@ class SettingMatches {
readonly matches: IRange[];
constructor(searchString: string, setting: ISetting, private requireFullQueryMatch: boolean, private searchDescription, private valuesMatcher: (filter: string, setting: ISetting) => IRange[]) {
constructor(searchString: string, setting: ISetting, private requireFullQueryMatch: boolean, private searchDescription: boolean, private valuesMatcher: (filter: string, setting: ISetting) => IRange[]) {
this.matches = distinct(this._findMatchesInSetting(searchString, setting), (match) => `${match.startLineNumber}_${match.startColumn}_${match.endLineNumber}_${match.endColumn}_`);
}

View File

@@ -11,7 +11,7 @@ import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cance
import * as collections from 'vs/base/common/collections';
import { getErrorMessage, isPromiseCanceledError } from 'vs/base/common/errors';
import { Iterator } from 'vs/base/common/iterator';
import { isArray } from 'vs/base/common/types';
import { isArray, withNullAsUndefined } from 'vs/base/common/types';
import { URI } from 'vs/base/common/uri';
import 'vs/css!./media/settingsEditor2';
import { localize } from 'vs/nls';
@@ -573,7 +573,7 @@ export class SettingsEditor2 extends BaseEditor {
this.tocTree.setSelection(element ? [element] : []);
if (this.searchResultModel) {
if (this.viewState.filterToCategory !== element) {
this.viewState.filterToCategory = element || undefined;
this.viewState.filterToCategory = withNullAsUndefined(element);
this.renderTree();
this.settingsTree.scrollTop = 0;
}

View File

@@ -74,8 +74,8 @@ export class GotoLineAction extends QuickOpenAction {
}
class GotoLineEntry extends EditorQuickOpenEntry {
private line: number;
private column: number;
private line!: number;
private column!: number;
private handler: GotoLineHandler;
constructor(line: string, editorService: IEditorService, handler: GotoLineHandler) {
@@ -140,7 +140,7 @@ class GotoLineEntry extends EditorQuickOpenEntry {
}
getInput(): IEditorInput | null {
return this.editorService.activeEditor || null;
return types.withUndefinedAsNull(this.editorService.activeEditor);
}
getOptions(pinned?: boolean): ITextEditorOptions {
@@ -218,8 +218,8 @@ export class GotoLineHandler extends QuickOpenHandler {
static readonly ID = 'workbench.picker.line';
private rangeHighlightDecorationId: IEditorLineDecoration | null;
private lastKnownEditorViewState: IEditorViewState | null;
private rangeHighlightDecorationId: IEditorLineDecoration | null = null;
private lastKnownEditorViewState: IEditorViewState | null = null;
constructor(@IEditorService private readonly editorService: IEditorService) {
super();

View File

@@ -289,7 +289,7 @@ class SymbolEntry extends EditorQuickOpenEntryGroup {
}
getInput(): IEditorInput | null {
return this.editorService.activeEditor || null;
return types.withUndefinedAsNull(this.editorService.activeEditor);
}
getOptions(pinned?: boolean): ITextEditorOptions {

View File

@@ -137,7 +137,7 @@ export class ViewPickerHandler extends QuickOpenHandler {
const result: ViewEntry[] = [];
if (views.length) {
for (const view of views) {
if (this.contextKeyService.contextMatchesRules(view.when || null)) {
if (this.contextKeyService.contextMatchesRules(view.when)) {
result.push(new ViewEntry(view.name, viewlet.name, () => this.viewsService.openView(view.id, true)));
}
}

View File

@@ -294,9 +294,9 @@ class DirtyDiffWidget extends PeekViewWidget {
};
}
getActionItem(action: IAction): IActionItem | null {
getActionItem(action: IAction): IActionItem | undefined {
if (!(action instanceof MenuItemAction)) {
return null;
return undefined;
}
return new DiffMenuItemActionItem(action, this.keybindingService, this.notificationService, this.contextMenuService);

View File

@@ -918,9 +918,9 @@ export class RepositoryPanel extends ViewletPanel {
return this.menus.getTitleSecondaryActions();
}
getActionItem(action: IAction): IActionItem | null {
getActionItem(action: IAction): IActionItem | undefined {
if (!(action instanceof MenuItemAction)) {
return null;
return undefined;
}
return new ContextAwareMenuItemActionItem(action, this.keybindingService, this.notificationService, this.contextMenuService);
@@ -1221,9 +1221,9 @@ export class SCMViewlet extends ViewContainerViewlet implements IViewModel {
}
}
getActionItem(action: IAction): IActionItem | null {
getActionItem(action: IAction): IActionItem | undefined {
if (!(action instanceof MenuItemAction)) {
return null;
return undefined;
}
return new ContextAwareMenuItemActionItem(action, this.keybindingService, this.notificationService, this.contextMenuService);

View File

@@ -25,6 +25,7 @@ import { ILabelService } from 'vs/platform/label/common/label';
import { CancellationToken } from 'vs/base/common/cancellation';
import { Schemas } from 'vs/base/common/network';
import { IOpenerService } from 'vs/platform/opener/common/opener';
import { withUndefinedAsNull } from 'vs/base/common/types';
class SymbolEntry extends EditorQuickOpenEntry {
private bearingResolve: Promise<this | undefined>;
@@ -58,7 +59,7 @@ class SymbolEntry extends EditorQuickOpenEntry {
return this.labelService.getUriLabel(this.bearing.location.uri, { relative: true });
}
return containerName || null;
return withUndefinedAsNull(containerName);
}
getIcon(): string {

View File

@@ -26,6 +26,7 @@ import { overviewRulerFindMatchForeground } from 'vs/platform/theme/common/color
import { themeColorFromId } from 'vs/platform/theme/common/themeService';
import { IReplaceService } from 'vs/workbench/contrib/search/common/replace';
import { editorMatchesToTextSearchResults } from 'vs/workbench/services/search/common/searchHelpers';
import { withNullAsUndefined } from 'vs/base/common/types';
export class Match {
@@ -440,7 +441,7 @@ export class BaseFolderMatch extends Disposable {
}
name(): string {
return getBaseLabel(this.resource() || undefined) || '';
return getBaseLabel(withNullAsUndefined(this.resource())) || '';
}
parent(): SearchResult {

View File

@@ -7,7 +7,7 @@ import { localize } from 'vs/nls';
import * as crypto from 'crypto';
import { onUnexpectedError } from 'vs/base/common/errors';
import { URI } from 'vs/base/common/uri';
import { IFileService, IFileStat, IResolveFileResult } from 'vs/platform/files/common/files';
import { IFileService, IFileStat, IResolveFileResult, IContent } from 'vs/platform/files/common/files';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
@@ -196,12 +196,15 @@ export function getHashedRemotesFromConfig(text: string, stripEndingDotGit: bool
export function getHashedRemotesFromUri(workspaceUri: URI, fileService: IFileService, stripEndingDotGit: boolean = false): Promise<string[]> {
const path = workspaceUri.path;
const uri = workspaceUri.with({ path: `${path !== '/' ? path : ''}/.git/config` });
return fileService.resolveFile(uri).then(() => {
return fileService.existsFile(uri).then(exists => {
if (!exists) {
return [];
}
return fileService.resolveContent(uri, { acceptTextOnly: true }).then(
content => getHashedRemotesFromConfig(content.value, stripEndingDotGit),
err => [] // ignore missing or binary file
);
}, err => []);
});
}
export class WorkspaceStats implements IWorkbenchContribution {
@@ -431,10 +434,14 @@ export class WorkspaceStats implements IWorkbenchContribution {
tags['workspace.android.cpp'] = true;
}
function getFilePromises(filename, fileService, contentHandler): Promise<void>[] {
function getFilePromises(filename: string, fileService: IFileService, contentHandler: (content: IContent) => void): Promise<void>[] {
return !nameSet.has(filename) ? [] : (folders as URI[]).map(workspaceUri => {
const uri = workspaceUri.with({ path: `${workspaceUri.path !== '/' ? workspaceUri.path : ''}/${filename}` });
return fileService.resolveFile(uri).then(() => {
return fileService.existsFile(uri).then(exists => {
if (!exists) {
return undefined;
}
return fileService.resolveContent(uri, { acceptTextOnly: true }).then(contentHandler);
}, err => {
// Ignore missing file
@@ -613,12 +620,15 @@ export class WorkspaceStats implements IWorkbenchContribution {
Promise.all<string[]>(workspaceUris.map(workspaceUri => {
const path = workspaceUri.path;
const uri = workspaceUri.with({ path: `${path !== '/' ? path : ''}/.git/config` });
return this.fileService.resolveFile(uri).then(() => {
return this.fileService.existsFile(uri).then(exists => {
if (!exists) {
return [];
}
return this.fileService.resolveContent(uri, { acceptTextOnly: true }).then(
content => getDomainsOfRemotes(content.value, SecondLevelDomainWhitelist),
err => [] // ignore missing or binary file
);
}, err => []);
});
})).then(domains => {
const set = domains.reduce((set, list) => list.reduce((set, item) => set.add(item), set), new Set<string>());
const list: string[] = [];
@@ -679,12 +689,15 @@ export class WorkspaceStats implements IWorkbenchContribution {
return Promise.all(workspaceUris.map(workspaceUri => {
const path = workspaceUri.path;
const uri = workspaceUri.with({ path: `${path !== '/' ? path : ''}/pom.xml` });
return this.fileService.resolveFile(uri).then(stats => {
return this.fileService.existsFile(uri).then(exists => {
if (!exists) {
return false;
}
return this.fileService.resolveContent(uri, { acceptTextOnly: true }).then(
content => !!content.value.match(/azure/i),
err => false
);
}, err => false);
});
})).then(javas => {
if (javas.indexOf(true) !== -1) {
tags['java'] = true;

View File

@@ -841,7 +841,9 @@ export class ProblemPatternParser extends Parser {
private createSingleProblemPattern(value: Config.CheckedProblemPattern): ProblemPattern | null {
let result = this.doCreateSingleProblemPattern(value, true);
if (result.kind === undefined) {
if (result === undefined) {
return null;
} else if (result.kind === undefined) {
result.kind = ProblemLocationKind.Location;
}
return this.validateProblemPattern([result]) ? result : null;
@@ -864,6 +866,9 @@ export class ProblemPatternParser extends Parser {
let result: MultiLineProblemPattern = [];
for (let i = 0; i < values.length; i++) {
let pattern = this.doCreateSingleProblemPattern(values[i], false);
if (pattern === undefined) {
return null;
}
if (i < values.length - 1) {
if (!Types.isUndefined(pattern.loop) && pattern.loop) {
pattern.loop = false;
@@ -878,10 +883,10 @@ export class ProblemPatternParser extends Parser {
return this.validateProblemPattern(result) ? result : null;
}
private doCreateSingleProblemPattern(value: Config.CheckedProblemPattern, setDefaults: boolean): ProblemPattern {
private doCreateSingleProblemPattern(value: Config.CheckedProblemPattern, setDefaults: boolean): ProblemPattern | undefined {
const regexp = this.createRegularExpression(value.regexp);
if (regexp === undefined) {
throw new Error('Invalid regular expression');
return undefined;
}
let result: ProblemPattern = { regexp };
if (value.kind) {
@@ -1680,7 +1685,7 @@ export interface IProblemMatcherRegistry {
onReady(): Promise<void>;
get(name: string): NamedProblemMatcher;
keys(): string[];
readonly onMatcherChanged;
readonly onMatcherChanged: Event<void>;
}
class ProblemMatcherRegistryImpl implements IProblemMatcherRegistry {

View File

@@ -18,11 +18,8 @@ import {
} from 'vs/workbench/contrib/tasks/common/problemMatcher';
import { IWorkspaceFolder } from 'vs/platform/workspace/common/workspace';
import * as Tasks from '../common/tasks';
import { TaskDefinitionRegistry } from '../common/taskDefinitionRegistry';
import { TaskDefinition } from 'vs/workbench/contrib/tasks/node/tasks';
import * as Tasks from './tasks';
import { TaskDefinitionRegistry } from './taskDefinitionRegistry';
import { ConfiguredInput } from 'vs/workbench/services/configurationResolver/common/configurationResolver';
export const enum ShellQuoting {
@@ -932,7 +929,7 @@ namespace CommandConfiguration {
}
function fromBase(this: void, config: BaseCommandConfiguationShape, context: ParseContext): Tasks.CommandConfiguration | undefined {
let name: Tasks.CommandString = ShellString.from(config.command)!;
let name: Tasks.CommandString | undefined = ShellString.from(config.command);
let runtime: Tasks.RuntimeType;
if (Types.isString(config.type)) {
if (config.type === 'shell' || config.type === 'process') {
@@ -947,7 +944,7 @@ namespace CommandConfiguration {
}
let result: Tasks.CommandConfiguration = {
name: name!,
name: name,
runtime: runtime!,
presentation: PresentationOptions.from(config, context)!
};
@@ -1200,7 +1197,7 @@ namespace TaskDependency {
if (Types.isString(external)) {
return { workspaceFolder: context.workspaceFolder, task: external };
} else if (TaskIdentifier.is(external)) {
return { workspaceFolder: context.workspaceFolder, task: TaskDefinition.createTaskIdentifier(external as Tasks.TaskIdentifier, context.problemReporter) };
return { workspaceFolder: context.workspaceFolder, task: Tasks.TaskDefinition.createTaskIdentifier(external as Tasks.TaskIdentifier, context.problemReporter) };
} else {
return undefined;
}
@@ -1332,7 +1329,7 @@ namespace ConfiguringTask {
));
return undefined;
}
let taskIdentifier: Tasks.KeyedTaskIdentifier | undefined = TaskDefinition.createTaskIdentifier(identifier, context.problemReporter);
let taskIdentifier: Tasks.KeyedTaskIdentifier | undefined = Tasks.TaskDefinition.createTaskIdentifier(identifier, context.problemReporter);
if (taskIdentifier === undefined) {
context.problemReporter.error(nls.localize(
'ConfigurationParser.incorrectType',

View File

@@ -74,7 +74,7 @@ export interface ITaskService {
getRecentlyUsedTasks(): LinkedMap<string, string>;
createSorter(): TaskSorter;
needsFolderQualification();
needsFolderQualification(): boolean;
canCustomize(task: ContributedTask | CustomTask): boolean;
customize(task: ContributedTask | CustomTask, properties?: {}, openConfig?: boolean): Promise<void>;
openConfig(task: CustomTask | undefined): Promise<void>;

View File

@@ -3,6 +3,7 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as nls from 'vs/nls';
import * as Types from 'vs/base/common/types';
import { IJSONSchemaMap } from 'vs/base/common/jsonSchema';
import * as Objects from 'vs/base/common/objects';
@@ -12,6 +13,7 @@ import { ProblemMatcher } from 'vs/workbench/contrib/tasks/common/problemMatcher
import { IWorkspaceFolder } from 'vs/platform/workspace/common/workspace';
import { RawContextKey } from 'vs/platform/contextkey/common/contextkey';
import { IExtensionDescription } from 'vs/platform/extensions/common/extensions';
import { TaskDefinitionRegistry } from 'vs/workbench/contrib/tasks/common/taskDefinitionRegistry';
export const TASK_RUNNING_STATE = new RawContextKey<boolean>('taskRunning', false);
@@ -341,7 +343,7 @@ export interface TaskSourceConfigElement {
}
interface BaseTaskSource {
readonly kind;
readonly kind: string;
readonly label: string;
}
@@ -467,7 +469,7 @@ export abstract class CommonTask {
*/
_label: string;
type;
type?: string;
runOptions: RunOptions;
@@ -477,7 +479,7 @@ export abstract class CommonTask {
private _taskLoadMessages: string[] | undefined;
protected constructor(id: string, label: string | undefined, type, runOptions: RunOptions,
protected constructor(id: string, label: string | undefined, type: string | undefined, runOptions: RunOptions,
configurationProperties: ConfigurationProperties, source: BaseTaskSource) {
this._id = id;
if (label) {
@@ -575,7 +577,7 @@ export class CustomTask extends CommonTask {
*/
command: CommandConfiguration;
public constructor(id: string, source: WorkspaceTaskSource, label: string, type, command: CommandConfiguration | undefined,
public constructor(id: string, source: WorkspaceTaskSource, label: string, type: string, command: CommandConfiguration | undefined,
hasDefinedMatchers: boolean, runOptions: RunOptions, configurationProperties: ConfigurationProperties) {
super(id, label, undefined, runOptions, configurationProperties, source);
this._source = source;
@@ -677,7 +679,7 @@ export class ConfiguringTask extends CommonTask {
configures: KeyedTaskIdentifier;
public constructor(id: string, source: WorkspaceTaskSource, label: string | undefined, type,
public constructor(id: string, source: WorkspaceTaskSource, label: string | undefined, type: string | undefined,
configures: KeyedTaskIdentifier, runOptions: RunOptions, configurationProperties: ConfigurationProperties) {
super(id, label, type, runOptions, configurationProperties, source);
this._source = source;
@@ -710,7 +712,7 @@ export class ContributedTask extends CommonTask {
*/
command: CommandConfiguration;
public constructor(id: string, source: ExtensionTaskSource, label: string, type, defines: KeyedTaskIdentifier,
public constructor(id: string, source: ExtensionTaskSource, label: string, type: string | undefined, defines: KeyedTaskIdentifier,
command: CommandConfiguration, hasDefinedMatchers: boolean, runOptions: RunOptions,
configurationProperties: ConfigurationProperties) {
super(id, label, type, runOptions, configurationProperties, source);
@@ -770,7 +772,7 @@ export class InMemoryTask extends CommonTask {
type: 'inMemory';
public constructor(id: string, source: InMemoryTaskSource, label: string, type,
public constructor(id: string, source: InMemoryTaskSource, label: string, type: string,
runOptions: RunOptions, configurationProperties: ConfigurationProperties) {
super(id, label, type, runOptions, configurationProperties, source);
this._source = source;
@@ -921,4 +923,76 @@ export namespace TaskEvent {
return Object.freeze({ kind: TaskEventKind.Changed });
}
}
}
}
export namespace KeyedTaskIdentifier {
function sortedStringify(literal: any): string {
const keys = Object.keys(literal).sort();
let result: string = '';
for (let position in keys) {
let stringified = literal[keys[position]];
if (stringified instanceof Object) {
stringified = sortedStringify(test);
} else if (typeof stringified === 'string') {
stringified = stringified.replace(/,/g, ',,');
}
result += keys[position] + ',' + stringified + ',';
}
return result;
}
export function create(value: TaskIdentifier): KeyedTaskIdentifier {
const resultKey = sortedStringify(value);
console.log(resultKey);
return { _key: resultKey, type: value.taskType };
}
}
export namespace TaskDefinition {
export function createTaskIdentifier(external: TaskIdentifier, reporter: { error(message: string): void; }): KeyedTaskIdentifier | undefined {
let definition = TaskDefinitionRegistry.get(external.type);
if (definition === undefined) {
// We have no task definition so we can't sanitize the literal. Take it as is
let copy = Objects.deepClone(external);
delete copy._key;
return KeyedTaskIdentifier.create(copy);
}
let literal: { type: string;[name: string]: any } = Object.create(null);
literal.type = definition.taskType;
let required: Set<string> = new Set();
definition.required.forEach(element => required.add(element));
let properties = definition.properties;
for (let property of Object.keys(properties)) {
let value = external[property];
if (value !== undefined && value !== null) {
literal[property] = value;
} else if (required.has(property)) {
let schema = properties[property];
if (schema.default !== undefined) {
literal[property] = Objects.deepClone(schema.default);
} else {
switch (schema.type) {
case 'boolean':
literal[property] = false;
break;
case 'number':
case 'integer':
literal[property] = 0;
break;
case 'string':
literal[property] = '';
break;
default:
reporter.error(nls.localize(
'TaskDefinition.missingRequiredProperty',
'Error: the task identifier \'{0}\' is missing the required property \'{1}\'. The task identifier will be ignored.', JSON.stringify(external, undefined, 0), property
));
return undefined;
}
}
}
}
return KeyedTaskIdentifier.create(literal);
}
}

View File

@@ -73,14 +73,13 @@ import { ITaskSystem, ITaskResolver, ITaskSummary, TaskExecuteKind, TaskError, T
import {
Task, CustomTask, ConfiguringTask, ContributedTask, InMemoryTask, TaskEvent,
TaskEventKind, TaskSet, TaskGroup, GroupType, ExecutionEngine, JsonSchemaVersion, TaskSourceKind,
TaskSorter, TaskIdentifier, KeyedTaskIdentifier, TASK_RUNNING_STATE, TaskRunSource
TaskSorter, TaskIdentifier, KeyedTaskIdentifier, TASK_RUNNING_STATE, TaskRunSource,
KeyedTaskIdentifier as NKeyedTaskIdentifier, TaskDefinition
} from 'vs/workbench/contrib/tasks/common/tasks';
import { ITaskService, ITaskProvider, ProblemMatcherRunOptions, CustomizationProperties, TaskFilter, WorkspaceFolderTaskResult } from 'vs/workbench/contrib/tasks/common/taskService';
import { getTemplates as getTaskTemplates } from 'vs/workbench/contrib/tasks/common/taskTemplates';
import { KeyedTaskIdentifier as NKeyedTaskIdentifier, TaskDefinition } from 'vs/workbench/contrib/tasks/node/tasks';
import * as TaskConfig from '../node/taskConfiguration';
import * as TaskConfig from '../common/taskConfiguration';
import { ProcessTaskSystem } from 'vs/workbench/contrib/tasks/node/processTaskSystem';
import { TerminalTaskSystem } from './terminalTaskSystem';
import { ProcessRunnerDetector } from 'vs/workbench/contrib/tasks/node/processRunnerDetector';
@@ -156,9 +155,9 @@ class BuildStatusBarItem extends Themable implements IStatusbarItem {
const info = document.createElement('div');
const building = document.createElement('div');
const errorTitle = n => nls.localize('totalErrors', "{0} Errors", n);
const warningTitle = n => nls.localize('totalWarnings', "{0} Warnings", n);
const infoTitle = n => nls.localize('totalInfos', "{0} Infos", n);
const errorTitle = (n: number) => nls.localize('totalErrors', "{0} Errors", n);
const warningTitle = (n: number) => nls.localize('totalWarnings', "{0} Warnings", n);
const infoTitle = (n: number) => nls.localize('totalInfos', "{0} Infos", n);
Dom.addClass(element, 'task-statusbar-item');
element.title = nls.localize('problems', "Problems");
@@ -211,7 +210,7 @@ class BuildStatusBarItem extends Themable implements IStatusbarItem {
}));
const manyProblems = nls.localize('manyProblems', "10K+");
const packNumber = n => n > 9999 ? manyProblems : n > 999 ? n.toString().charAt(0) + 'K' : n.toString();
const packNumber = (n: number) => n > 9999 ? manyProblems : n > 999 ? n.toString().charAt(0) + 'K' : n.toString();
let updateLabel = (stats: MarkerStatistics) => {
error.innerHTML = packNumber(stats.errors);
error.title = errorIcon.title = errorTitle(stats.errors);

View File

@@ -1097,7 +1097,7 @@ export class TerminalTaskSystem implements ITaskSystem {
}
private collectTaskVariables(variables: Set<string>, task: CustomTask | ContributedTask): void {
if (task.command) {
if (task.command && task.command.name) {
this.collectCommandVariables(variables, task.command, task);
}
this.collectMatcherVariables(variables, task.configurationProperties.problemMatchers);

View File

@@ -14,7 +14,7 @@ import { IFileService } from 'vs/platform/files/common/files';
import { IWorkspaceContextService, IWorkspaceFolder, WorkbenchState } from 'vs/platform/workspace/common/workspace';
import { IConfigurationResolverService } from 'vs/workbench/services/configurationResolver/common/configurationResolver';
import * as Tasks from '../common/tasks';
import * as TaskConfig from './taskConfiguration';
import * as TaskConfig from '../common/taskConfiguration';
const build = 'build';
const test = 'test';

View File

@@ -319,7 +319,7 @@ export class ProcessTaskSystem implements ITaskSystem {
this.activeTask = task;
const inactiveEvent = TaskEvent.create(TaskEventKind.Inactive, task);
let processStartedSignaled: boolean = false;
const onProgress = (progress) => {
const onProgress = (progress: LineData) => {
let line = Strings.removeAnsiEscapeCodes(progress.line);
this.appendOutput(line + '\n');
startStopProblemMatcher.processLine(line);

View File

@@ -1,75 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as nls from 'vs/nls';
import * as crypto from 'crypto';
import * as Objects from 'vs/base/common/objects';
import { TaskIdentifier, KeyedTaskIdentifier, TaskDefinition } from 'vs/workbench/contrib/tasks/common/tasks';
import { TaskDefinitionRegistry } from 'vs/workbench/contrib/tasks/common/taskDefinitionRegistry';
namespace KeyedTaskIdentifier {
export function create(value: TaskIdentifier): KeyedTaskIdentifier {
const hash = crypto.createHash('md5');
hash.update(JSON.stringify(value));
let result = { _key: hash.digest('hex'), type: value.taskType };
Objects.assign(result, value);
return result;
}
}
namespace TaskDefinition {
export function createTaskIdentifier(external: TaskIdentifier, reporter: { error(message: string): void; }): KeyedTaskIdentifier | undefined {
let definition = TaskDefinitionRegistry.get(external.type);
if (definition === undefined) {
// We have no task definition so we can't sanitize the literal. Take it as is
let copy = Objects.deepClone(external);
delete copy._key;
return KeyedTaskIdentifier.create(copy);
}
let literal: { type: string;[name: string]: any } = Object.create(null);
literal.type = definition.taskType;
let required: Set<string> = new Set();
definition.required.forEach(element => required.add(element));
let properties = definition.properties;
for (let property of Object.keys(properties)) {
let value = external[property];
if (value !== undefined && value !== null) {
literal[property] = value;
} else if (required.has(property)) {
let schema = properties[property];
if (schema.default !== undefined) {
literal[property] = Objects.deepClone(schema.default);
} else {
switch (schema.type) {
case 'boolean':
literal[property] = false;
break;
case 'number':
case 'integer':
literal[property] = 0;
break;
case 'string':
literal[property] = '';
break;
default:
reporter.error(nls.localize(
'TaskDefinition.missingRequiredProperty',
'Error: the task identifier \'{0}\' is missing the required property \'{1}\'. The task identifier will be ignored.', JSON.stringify(external, undefined, 0), property
));
return undefined;
}
}
}
}
return KeyedTaskIdentifier.create(literal);
}
}
export { KeyedTaskIdentifier, TaskDefinition };

View File

@@ -13,7 +13,7 @@ import { ProblemMatcher, FileLocationKind, ProblemPattern, ApplyToKind } from 'v
import { IWorkspaceFolder, WorkspaceFolder } from 'vs/platform/workspace/common/workspace';
import * as Tasks from 'vs/workbench/contrib/tasks/common/tasks';
import { parse, ParseResult, IProblemReporter, ExternalTaskRunnerConfiguration, CustomTask } from 'vs/workbench/contrib/tasks/node/taskConfiguration';
import { parse, ParseResult, IProblemReporter, ExternalTaskRunnerConfiguration, CustomTask } from 'vs/workbench/contrib/tasks/common/taskConfiguration';
const workspaceFolder: IWorkspaceFolder = new WorkspaceFolder({
uri: URI.file('/workspace/folderOne'),

View File

@@ -1,6 +1,6 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { KeyCode, KeyMod } from 'vs/base/common/keyCodes';

View File

@@ -34,6 +34,7 @@ import { IHistoryService } from 'vs/workbench/services/history/common/history';
import { Schemas } from 'vs/base/common/network';
import { URI } from 'vs/base/common/uri';
import { isWindows } from 'vs/base/common/platform';
import { withNullAsUndefined } from 'vs/base/common/types';
export const TERMINAL_PICKER_PREFIX = 'term ';
@@ -291,7 +292,7 @@ export class SendSequenceTerminalCommand extends Command {
const workspaceContextService = accessor.get(IWorkspaceContextService);
const historyService = accessor.get(IHistoryService);
const activeWorkspaceRootUri = historyService.getLastActiveWorkspaceRoot(Schemas.file);
const lastActiveWorkspaceRoot = activeWorkspaceRootUri ? workspaceContextService.getWorkspaceFolder(activeWorkspaceRootUri) || undefined : undefined;
const lastActiveWorkspaceRoot = activeWorkspaceRootUri ? withNullAsUndefined(workspaceContextService.getWorkspaceFolder(activeWorkspaceRootUri)) : undefined;
const resolvedText = configurationResolverService.resolve(lastActiveWorkspaceRoot, args.text);
terminalInstance.sendText(resolvedText, false);
}

View File

@@ -894,9 +894,12 @@ export class TerminalInstance implements ITerminalInstance {
if (platform.isWindows) {
this._processManager.ptyProcessReady.then(() => {
if (this._processManager!.remoteAuthority) {
return;
}
this._xtermReadyPromise.then(() => {
if (!this._isDisposed) {
this._terminalInstanceService.createWindowsShellHelper(this._processManager!.shellProcessId, this, this._xterm);
this._windowsShellHelper = this._terminalInstanceService.createWindowsShellHelper(this._processManager!.shellProcessId, this, this._xterm);
}
});
});

View File

@@ -161,7 +161,7 @@ export class TerminalPanel extends Panel {
return this._contextMenuActions;
}
public getActionItem(action: Action): IActionItem | null {
public getActionItem(action: Action): IActionItem | undefined {
if (action.id === SwitchTerminalAction.ID) {
return this._instantiationService.createInstance(SwitchTerminalActionItem, action);
}

View File

@@ -22,7 +22,7 @@ import { IEnvironmentService } from 'vs/platform/environment/common/environment'
import { IProductService } from 'vs/platform/product/common/product';
import { ITerminalInstanceService } from 'vs/workbench/contrib/terminal/browser/terminal';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { IRemoteEnvironmentService } from 'vs/workbench/services/remote/common/remoteEnvironmentService';
import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService';
/** The amount of time to consider terminal errors to be related to the launch */
const LAUNCHING_DURATION = 500;
@@ -77,7 +77,7 @@ export class TerminalProcessManager implements ITerminalProcessManager {
@IEnvironmentService private readonly _environmentService: IEnvironmentService,
@IProductService private readonly _productService: IProductService,
@ITerminalInstanceService private readonly _terminalInstanceService: ITerminalInstanceService,
@IRemoteEnvironmentService private readonly _remoteEnvironmentService: IRemoteEnvironmentService
@IRemoteAgentService private readonly _remoteAgentService: IRemoteAgentService
) {
this.ptyProcessReady = new Promise<void>(c => {
this.onProcessReady(() => {
@@ -123,7 +123,7 @@ export class TerminalProcessManager implements ITerminalProcessManager {
this.os = platform.OS;
if (launchRemotely) {
if (hasRemoteAuthority) {
this._remoteEnvironmentService.remoteEnvironment.then(env => {
this._remoteAgentService.getEnvironment().then(env => {
if (!env) {
return;
}
@@ -250,6 +250,7 @@ export class TerminalProcessManager implements ITerminalProcessManager {
}
public async getLatency(): Promise<number> {
await this.ptyProcessReady;
if (!this._process) {
return Promise.resolve(0);
}

View File

@@ -18,7 +18,7 @@ function getMockTheme(type: ThemeType): ITheme {
selector: '',
label: '',
type: type,
getColor: (colorId: ColorIdentifier) => themingRegistry.resolveDefaultColor(colorId, theme),
getColor: (colorId: ColorIdentifier): Color | undefined => themingRegistry.resolveDefaultColor(colorId, theme),
defines: () => true
};
return theme;

View File

@@ -53,7 +53,7 @@ export class SelectColorThemeAction extends Action {
//configurationEntries(this.extensionGalleryService, localize('installColorThemes', "Install Additional Color Themes..."))
);
const selectTheme = (theme, applyTheme: boolean) => {
const selectTheme = (theme: IColorTheme, applyTheme: boolean) => {
if (typeof theme.id === 'undefined') { // 'pick in marketplace' entry
if (applyTheme) {
openExtensionViewlet(this.viewletService, 'category:themes ');

View File

@@ -245,6 +245,7 @@ export class WebviewEditor extends BaseEditor {
webview.update(input.html, {
allowScripts: input.options.enableScripts,
localResourceRoots: input.options.localResourceRoots || this.getDefaultLocalResourceRoots(),
portMappings: input.options.portMapping,
}, !!input.options.retainContextWhenHidden);
if (this._webviewContent) {

View File

@@ -9,9 +9,8 @@ import { URI } from 'vs/base/common/uri';
import { IEditorModel } from 'vs/platform/editor/common/editor';
import { EditorInput, EditorModel, GroupIdentifier, IEditorInput } from 'vs/workbench/common/editor';
import { IWorkbenchLayoutService, Parts } from 'vs/workbench/services/layout/browser/layoutService';
import * as vscode from 'vscode';
import { WebviewEvents, WebviewInputOptions } from './webviewEditorService';
import { WebviewElement } from './webviewElement';
import { WebviewElement, WebviewOptions } from './webviewElement';
export class WebviewEditorInput extends EditorInput {
private static handlePool = 0;
@@ -195,7 +194,7 @@ export class WebviewEditorInput extends EditorInput {
return this._options;
}
public setOptions(value: vscode.WebviewOptions) {
public setOptions(value: WebviewOptions) {
this._options = {
...this._options,
...value
@@ -204,7 +203,8 @@ export class WebviewEditorInput extends EditorInput {
if (this._webview) {
this._webview.options = {
allowScripts: this._options.enableScripts,
localResourceRoots: this._options.localResourceRoots
localResourceRoots: this._options.localResourceRoots,
portMappings: this._options.portMapping,
};
}
}

View File

@@ -35,9 +35,9 @@ export class WebviewEditorInputFactory implements IEditorInputFactory {
public serialize(
input: WebviewEditorInput
): string | null {
): string | undefined {
if (!this._webviewService.shouldPersist(input)) {
return null;
return undefined;
}
const data: SerializedWebview = {
@@ -54,7 +54,7 @@ export class WebviewEditorInputFactory implements IEditorInputFactory {
try {
return JSON.stringify(data);
} catch {
return null;
return undefined;
}
}

View File

@@ -11,8 +11,8 @@ import { createDecorator, IInstantiationService } from 'vs/platform/instantiatio
import { GroupIdentifier } from 'vs/workbench/common/editor';
import { IEditorGroup, IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService';
import { ACTIVE_GROUP_TYPE, IEditorService, SIDE_GROUP_TYPE } from 'vs/workbench/services/editor/common/editorService';
import * as vscode from 'vscode';
import { RevivedWebviewEditorInput, WebviewEditorInput } from './webviewEditorInput';
import { IWebviewOptions, IWebviewPanelOptions } from 'vs/editor/common/modes';
export const IWebviewEditorService = createDecorator<IWebviewEditorService>('webviewEditorService');
@@ -72,10 +72,10 @@ export interface WebviewReviver {
export interface WebviewEvents {
onMessage?(message: any): void;
onDispose?(): void;
onDidClickLink?(link: URI, options: vscode.WebviewOptions): void;
onDidClickLink?(link: URI, options: IWebviewOptions): void;
}
export interface WebviewInputOptions extends vscode.WebviewOptions, vscode.WebviewPanelOptions {
export interface WebviewInputOptions extends IWebviewOptions, IWebviewPanelOptions {
tryRestoreScrollPosition?: boolean;
}
@@ -85,7 +85,8 @@ export function areWebviewInputOptionsEqual(a: WebviewInputOptions, b: WebviewIn
&& a.enableScripts === b.enableScripts
&& a.retainContextWhenHidden === b.retainContextWhenHidden
&& a.tryRestoreScrollPosition === b.tryRestoreScrollPosition
&& (a.localResourceRoots === b.localResourceRoots || (Array.isArray(a.localResourceRoots) && Array.isArray(b.localResourceRoots) && equals(a.localResourceRoots, b.localResourceRoots, (a, b) => a.toString() === b.toString())));
&& (a.localResourceRoots === b.localResourceRoots || (Array.isArray(a.localResourceRoots) && Array.isArray(b.localResourceRoots) && equals(a.localResourceRoots, b.localResourceRoots, (a, b) => a.toString() === b.toString())))
&& (a.portMapping === b.portMapping || (Array.isArray(a.portMapping) && Array.isArray(b.portMapping) && equals(a.portMapping, b.portMapping, (a, b) => a.from === b.from && a.to === b.to)));
}
function canRevive(reviver: WebviewReviver, webview: WebviewEditorInput): boolean {
@@ -128,11 +129,11 @@ export class WebviewEditorService implements IWebviewEditorService {
viewType: string,
title: string,
showOptions: ICreateWebViewShowOptions,
options: vscode.WebviewOptions,
options: IWebviewOptions,
extensionLocation: URI | undefined,
events: WebviewEvents
): WebviewEditorInput {
const webviewInput = this._instantiationService.createInstance(WebviewEditorInput, viewType, undefined, title, options, {}, events, extensionLocation, undefined);
const webviewInput = this._instantiationService.createInstance(WebviewEditorInput, viewType, undefined, title, options, {}, events, extensionLocation);
this._editorService.openEditor(webviewInput, { pinned: true, preserveFocus: showOptions.preserveFocus }, showOptions.group);
return webviewInput;
}

View File

@@ -3,9 +3,13 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { OnBeforeRequestDetails, OnHeadersReceivedDetails, Response } from 'electron';
import { addClass, addDisposableListener } from 'vs/base/browser/dom';
import { Emitter, Event } from 'vs/base/common/event';
import { once } from 'vs/base/common/functional';
import { Disposable } from 'vs/base/common/lifecycle';
import { isMacintosh } from 'vs/base/common/platform';
import { endsWith } from 'vs/base/common/strings';
import { URI } from 'vs/base/common/uri';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { IFileService } from 'vs/platform/files/common/files';
@@ -15,8 +19,16 @@ import { DARK, ITheme, IThemeService, LIGHT } from 'vs/platform/theme/common/the
import { registerFileProtocol, WebviewProtocol } from 'vs/workbench/contrib/webview/electron-browser/webviewProtocols';
import { areWebviewInputOptionsEqual } from './webviewEditorService';
import { WebviewFindWidget } from './webviewFindWidget';
import { endsWith } from 'vs/base/common/strings';
import { isMacintosh } from 'vs/base/common/platform';
export interface WebviewPortMapping {
readonly port: number;
readonly resolvedPort: number;
}
export interface WebviewPortMapping {
readonly port: number;
readonly resolvedPort: number;
}
export interface WebviewOptions {
readonly allowSvgs?: boolean;
@@ -28,6 +40,7 @@ export interface WebviewContentOptions {
readonly allowScripts?: boolean;
readonly svgWhiteList?: string[];
readonly localResourceRoots?: ReadonlyArray<URI>;
readonly portMappings?: ReadonlyArray<WebviewPortMapping>;
}
interface IKeydownEvent {
@@ -41,6 +54,58 @@ interface IKeydownEvent {
repeat: boolean;
}
type OnBeforeRequestDelegate = (details: OnBeforeRequestDetails) => Promise<Response | undefined>;
type OnHeadersReceivedDelegate = (details: OnHeadersReceivedDetails) => { cancel: boolean } | undefined;
class WebviewSession extends Disposable {
private readonly _onBeforeRequestDelegates: Array<OnBeforeRequestDelegate> = [];
private readonly _onHeadersReceivedDelegates: Array<OnHeadersReceivedDelegate> = [];
public constructor(
webview: Electron.WebviewTag,
) {
super();
this._register(addDisposableListener(webview, 'did-start-loading', once(() => {
const contents = webview.getWebContents();
if (!contents) {
return;
}
contents.session.webRequest.onBeforeRequest(async (details, callback) => {
for (const delegate of this._onBeforeRequestDelegates) {
const result = await delegate(details);
if (typeof result !== 'undefined') {
callback(result);
return;
}
}
callback({});
});
contents.session.webRequest.onHeadersReceived((details, callback) => {
for (const delegate of this._onHeadersReceivedDelegates) {
const result = delegate(details);
if (typeof result !== 'undefined') {
callback(result);
return;
}
}
callback({ cancel: false, responseHeaders: details.responseHeaders });
});
})));
}
public onBeforeRequest(delegate: OnBeforeRequestDelegate) {
this._onBeforeRequestDelegates.push(delegate);
}
public onHeadersReceived(delegate: OnHeadersReceivedDelegate) {
this._onHeadersReceivedDelegates.push(delegate);
}
}
class WebviewProtocolProvider extends Disposable {
constructor(
webview: Electron.WebviewTag,
@@ -51,21 +116,15 @@ class WebviewProtocolProvider extends Disposable {
) {
super();
let loaded = false;
this._register(addDisposableListener(webview, 'did-start-loading', () => {
if (loaded) {
return;
}
loaded = true;
this._register(addDisposableListener(webview, 'did-start-loading', once(() => {
const contents = webview.getWebContents();
if (contents) {
this.registerFileProtocols(contents);
this.registerProtocols(contents);
}
}));
})));
}
private registerFileProtocols(contents: Electron.WebContents) {
private registerProtocols(contents: Electron.WebContents) {
if (contents.isDestroyed()) {
return;
}
@@ -82,52 +141,73 @@ class WebviewProtocolProvider extends Disposable {
}
}
class WebviewPortMappingProvider extends Disposable {
constructor(
session: WebviewSession,
mappings: () => ReadonlyArray<WebviewPortMapping>
) {
super();
session.onBeforeRequest(async (details) => {
const uri = URI.parse(details.url);
if (uri.scheme !== 'http' && uri.scheme !== 'https') {
return undefined;
}
const localhostMatch = /^localhost:(\d+)$/.exec(uri.authority);
if (localhostMatch) {
const port = +localhostMatch[1];
for (const mapping of mappings()) {
if (mapping.port === port && mapping.port !== mapping.resolvedPort) {
return {
redirectURL: details.url.replace(
new RegExp(`^${uri.scheme}://localhost:${mapping.port}/`),
`${uri.scheme}://localhost:${mapping.resolvedPort}/`)
};
}
}
}
return undefined;
});
}
}
class SvgBlocker extends Disposable {
private readonly _onDidBlockSvg = this._register(new Emitter<void>());
public readonly onDidBlockSvg = this._onDidBlockSvg.event;
constructor(
webview: Electron.WebviewTag,
session: WebviewSession,
private readonly _options: WebviewContentOptions,
) {
super();
let loaded = false;
this._register(addDisposableListener(webview, 'did-start-loading', () => {
if (loaded) {
return;
}
loaded = true;
const contents = webview.getWebContents();
if (!contents) {
return;
session.onBeforeRequest(async (details) => {
if (details.url.indexOf('.svg') > 0) {
const uri = URI.parse(details.url);
if (uri && !uri.scheme.match(/file/i) && endsWith(uri.path, '.svg') && !this.isAllowedSvg(uri)) {
this._onDidBlockSvg.fire();
return { cancel: true };
}
}
contents.session.webRequest.onBeforeRequest((details, callback) => {
if (details.url.indexOf('.svg') > 0) {
const uri = URI.parse(details.url);
if (uri && !uri.scheme.match(/file/i) && endsWith(uri.path, '.svg') && !this.isAllowedSvg(uri)) {
this._onDidBlockSvg.fire();
return callback({ cancel: true });
}
}
return callback({});
});
return undefined;
});
contents.session.webRequest.onHeadersReceived((details, callback) => {
const contentType: string[] = details.responseHeaders['content-type'] || details.responseHeaders['Content-Type'];
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.fire();
return callback({ cancel: true });
}
session.onHeadersReceived((details) => {
const contentType: string[] = details.responseHeaders['content-type'] || details.responseHeaders['Content-Type'];
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.fire();
return { cancel: true };
}
return callback({ cancel: false, responseHeaders: details.responseHeaders });
});
}));
}
return undefined;
});
}
private isAllowedSvg(uri: URI): boolean {
@@ -236,7 +316,7 @@ export class WebviewElement extends Disposable {
@IInstantiationService instantiationService: IInstantiationService,
@IThemeService themeService: IThemeService,
@IEnvironmentService environmentService: IEnvironmentService,
@IFileService fileService: IFileService
@IFileService fileService: IFileService,
) {
super();
this._webview = document.createElement('webview');
@@ -264,16 +344,23 @@ export class WebviewElement extends Disposable {
}));
});
this._register(
new WebviewProtocolProvider(
this._webview,
this._options.extensionLocation,
() => (this._contentOptions.localResourceRoots || []),
environmentService,
fileService));
const session = this._register(new WebviewSession(this._webview));
this._register(new WebviewProtocolProvider(
this._webview,
this._options.extensionLocation,
() => (this._contentOptions.localResourceRoots || []),
environmentService,
fileService));
this._register(new WebviewPortMappingProvider(
session,
() => (this._contentOptions.portMappings || [])
));
if (!this._options.allowSvgs) {
const svgBlocker = this._register(new SvgBlocker(this._webview, this._contentOptions));
const svgBlocker = this._register(new SvgBlocker(session, this._contentOptions));
svgBlocker.onDidBlockSvg(() => this.onDidBlockSvg());
}
@@ -496,7 +583,7 @@ export class WebviewElement extends Disposable {
if (!window || !window.webContents || window.webContents.isDestroyed()) {
return;
}
window.webContents.getZoomFactor(factor => {
window.webContents.getZoomFactor((factor: number) => {
if (contents.isDestroyed()) {
return;
}

View File

@@ -2,8 +2,8 @@
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { extname, sep } from 'vs/base/common/path';
import { getMediaMime, MIME_UNKNOWN } from 'vs/base/common/mime';
import { extname, sep } from 'vs/base/common/path';
import { startsWith } from 'vs/base/common/strings';
import { URI } from 'vs/base/common/uri';
import { IFileService } from 'vs/platform/files/common/files';
@@ -11,7 +11,7 @@ import { REMOTE_HOST_SCHEME } from 'vs/platform/remote/common/remoteHosts';
export const enum WebviewProtocol {
CoreResource = 'vscode-core-resource',
VsCodeResource = 'vscode-resource'
VsCodeResource = 'vscode-resource',
}
function resolveContent(fileService: IFileService, resource: URI, mime: string, callback: any): void {
@@ -60,7 +60,7 @@ export function registerFileProtocol(
callback({ error: -10 /* ACCESS_DENIED: https://cs.chromium.org/chromium/src/net/base/net_error_list.h */ });
}, (error) => {
if (error) {
console.error('Failed to register protocol ' + protocol);
console.error(`Failed to register '${protocol}' protocol`);
}
});
}

View File

@@ -14,7 +14,7 @@ export class GettingStarted implements IWorkbenchContribution {
private static readonly hideWelcomeSettingskey = 'workbench.hide.welcome';
private welcomePageURL: string;
private welcomePageURL?: string;
private appName: string;
constructor(

View File

@@ -1,32 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock';
import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
import { IStorageService } from 'vs/platform/storage/common/storage';
suite('Workbench - GettingStarted', () => {
let instantiation: TestInstantiationService | null = null;
let hideWelcomeSettingsValue: string | null = null;
suiteSetup(() => {
instantiation = new TestInstantiationService();
instantiation.stub(IWorkspaceContextService, {
});
instantiation.stub(IStorageService, <Partial<IStorageService>>{
get: () => hideWelcomeSettingsValue,
store: (value) => hideWelcomeSettingsValue = value
});
});
suiteTeardown(() => {
instantiation = null;
});
setup(() => {
hideWelcomeSettingsValue = null;
});
});

View File

@@ -48,7 +48,7 @@ export class WalkThroughInput extends EditorInput {
private disposables: IDisposable[] = [];
private promise: Promise<WalkThroughModel> | null;
private promise: Promise<WalkThroughModel> | null = null;
private maxTopScroll = 0;
private maxBottomScroll = 0;