Merge from vscode 777931080477e28b7c27e8f7d4b0d69897945946 (#9220)

This commit is contained in:
Anthony Dresser
2020-02-19 22:27:53 -08:00
committed by GitHub
parent ab6fb810f8
commit 0cec223301
115 changed files with 1431 additions and 1133 deletions

View File

@@ -25,6 +25,7 @@ import { basename } from 'vs/base/common/resources';
import { ThemeIcon } from 'vs/platform/theme/common/themeService';
import { WorkspaceFileEdit } from 'vs/editor/common/modes';
import { compare } from 'vs/base/common/strings';
import { URI } from 'vs/base/common/uri';
// --- VIEW MODEL
@@ -420,6 +421,12 @@ export class CategoryElementRenderer implements ITreeRenderer<CategoryElement, F
const className = ThemeIcon.asClassName(metadata.iconPath);
template.icon.className = className ? `theme-icon ${className}` : '';
} else if (URI.isUri(metadata.iconPath)) {
// background-image
template.icon.className = 'uri-icon';
template.icon.style.setProperty('--background-dark', `url("${metadata.iconPath.toString(true)}")`);
template.icon.style.setProperty('--background-light', `url("${metadata.iconPath.toString(true)}")`);
} else if (metadata.iconPath) {
// background-image
template.icon.className = 'uri-icon';

View File

@@ -7,7 +7,7 @@ import { coalesce } from 'vs/base/common/arrays';
import { Emitter } from 'vs/base/common/event';
import { Lazy } from 'vs/base/common/lazy';
import { Disposable } from 'vs/base/common/lifecycle';
import { basename, isEqual } from 'vs/base/common/resources';
import { basename, isEqual, extname } from 'vs/base/common/resources';
import { URI } from 'vs/base/common/uri';
import { generateUuid } from 'vs/base/common/uuid';
import * as nls from 'vs/nls';
@@ -180,21 +180,62 @@ export class CustomEditorService extends Disposable implements ICustomEditorServ
}
}
const resourceExt = extname(resource);
const items = customEditors.allEditors.map((editorDescriptor): IQuickPickItem => ({
label: editorDescriptor.displayName,
id: editorDescriptor.id,
description: editorDescriptor.id === currentlyOpenedEditorType
? nls.localize('openWithCurrentlyActive', "Currently Active")
: undefined
: undefined,
buttons: resourceExt ? [{
iconClass: 'codicon-settings-gear',
tooltip: nls.localize('promptOpenWith.setDefaultTooltip', "Set as default editor for '{0}' files", resourceExt)
}] : undefined
}));
const pick = await this.quickInputService.pick(items, {
placeHolder: nls.localize('promptOpenWith.placeHolder', "Select editor to use for '{0}'...", basename(resource)),
const picker = this.quickInputService.createQuickPick();
picker.items = items;
picker.placeholder = nls.localize('promptOpenWith.placeHolder', "Select editor to use for '{0}'...", basename(resource));
const pick = await new Promise<string | undefined>(resolve => {
picker.onDidAccept(() => {
resolve(picker.selectedItems.length === 1 ? picker.selectedItems[0].id : undefined);
picker.dispose();
});
picker.onDidTriggerItemButton(e => {
const pick = e.item.id;
resolve(pick); // open the view
picker.dispose();
// And persist the setting
if (pick) {
const newAssociation: CustomEditorAssociation = { viewType: pick, filenamePattern: '*' + resourceExt };
const currentAssociations = [...this.configurationService.getValue<CustomEditorsAssociations>(customEditorsAssociationsKey)] || [];
// First try updating existing association
for (let i = 0; i < currentAssociations.length; ++i) {
const existing = currentAssociations[i];
if (existing.filenamePattern === newAssociation.filenamePattern) {
currentAssociations.splice(i, 1, newAssociation);
this.configurationService.updateValue(customEditorsAssociationsKey, currentAssociations);
return;
}
}
// Otherwise, create a new one
currentAssociations.unshift(newAssociation);
this.configurationService.updateValue(customEditorsAssociationsKey, currentAssociations);
}
});
picker.show();
});
if (!pick || !pick.id) {
if (!pick) {
return undefined; // {{SQL CARBON EDIT}} strict-null-check
}
return this.openWith(resource, pick.id, options, group);
return this.openWith(resource, pick, options, group);
}
public openWith(
@@ -312,7 +353,11 @@ export class CustomEditorService extends Disposable implements ICustomEditorServ
export const customEditorsAssociationsKey = 'workbench.experimental.editorAssociations';
export type CustomEditorsAssociations = readonly (CustomEditorSelector & { readonly viewType: string; })[];
export type CustomEditorAssociation = CustomEditorSelector & {
readonly viewType: string;
};
export type CustomEditorsAssociations = readonly CustomEditorAssociation[];
export class CustomEditorContribution extends Disposable implements IWorkbenchContribution {
constructor(

View File

@@ -915,14 +915,13 @@ export class DebugSession implements IDebugSession {
// Disconnects and clears state. Session can be initialized again for a new connection.
private shutdown(): void {
dispose(this.rawListeners);
if (this.raw) {
this.raw.disconnect();
this.raw.dispose();
this.raw = undefined;
}
this.fetchThreadsScheduler = undefined;
this.model.clearThreads(this.getId(), true);
if (this.raw) {
const raw = this.raw;
this.raw = undefined;
raw.disconnect();
raw.dispose();
}
this._onDidChangeState.fire();
}

View File

@@ -13,31 +13,6 @@
height: 100%;
}
.debug-pane .debug-start-view {
padding: 0 20px 0 20px;
}
.debug-pane .debug-start-view .monaco-button,
.debug-pane .debug-start-view .section {
margin-top: 20px;
}
.debug-pane .debug-start-view .top-section {
margin-top: 10px;
}
.debug-pane .debug-start-view .monaco-button {
max-width: 260px;
margin-left: auto;
margin-right: auto;
display: block;
}
.debug-pane .debug-start-view .click {
cursor: pointer;
color: #007ACC;
}
.monaco-workbench .debug-action.notification:after {
content: '';
width: 6px;

View File

@@ -3,64 +3,33 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as dom from 'vs/base/browser/dom';
import { Button } from 'vs/base/browser/ui/button/button';
import { IViewletViewOptions } from 'vs/workbench/browser/parts/views/viewsViewlet';
import { attachButtonStyler } from 'vs/platform/theme/common/styler';
import { IThemeService } from 'vs/platform/theme/common/themeService';
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
import { IContextKeyService, RawContextKey, IContextKey } from 'vs/platform/contextkey/common/contextkey';
import { localize } from 'vs/nls';
import { ICommandService } from 'vs/platform/commands/common/commands';
import { StartAction, ConfigureAction } from 'vs/workbench/contrib/debug/browser/debugActions';
import { IDebugService } from 'vs/workbench/contrib/debug/common/debug';
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace';
import { IFileDialogService } from 'vs/platform/dialogs/common/dialogs';
import { equals } from 'vs/base/common/arrays';
import { IViewPaneOptions, ViewPane } from 'vs/workbench/browser/parts/views/viewPaneContainer';
import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent';
import { KeyCode } from 'vs/base/common/keyCodes';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { IViewDescriptorService } from 'vs/workbench/common/views';
import { IViewDescriptorService, IViewsRegistry, Extensions } from 'vs/workbench/common/views';
import { Registry } from 'vs/platform/registry/common/platform';
import { IOpenerService } from 'vs/platform/opener/common/opener';
const $ = dom.$;
import { WorkbenchStateContext } from 'vs/workbench/browser/contextkeys';
import { OpenFolderAction, OpenFileAction, OpenFileFolderAction } from 'vs/workbench/browser/actions/workspaceActions';
import { isMacintosh } from 'vs/base/common/platform';
interface DebugStartMetrics {
debuggers?: string[];
}
type DebugStartMetricsClassification = {
debuggers?: { classification: 'SystemMetaData', purpose: 'FeatureInsight' };
};
function createClickElement(textContent: string, action: () => any): HTMLSpanElement {
const clickElement = $('span.click');
clickElement.textContent = textContent;
clickElement.onclick = action;
clickElement.tabIndex = 0;
clickElement.onkeyup = (e) => {
const keyboardEvent = new StandardKeyboardEvent(e);
if (keyboardEvent.keyCode === KeyCode.Enter || (keyboardEvent.keyCode === KeyCode.Space)) {
action();
}
};
return clickElement;
}
const CONTEXT_DEBUGGER_INTERESTED = new RawContextKey<boolean>('debuggerInterested', false);
export class StartView extends ViewPane {
static ID = 'workbench.debug.startView';
static LABEL = localize('start', "Start");
private debugButton!: Button;
private firstMessageContainer!: HTMLElement;
private secondMessageContainer!: HTMLElement;
private clickElement: HTMLElement | undefined;
private debuggerLabels: string[] | undefined = undefined;
private debuggerInterestedContext: IContextKey<boolean>;
constructor(
options: IViewletViewOptions,
@@ -69,125 +38,45 @@ export class StartView extends ViewPane {
@IContextMenuService contextMenuService: IContextMenuService,
@IConfigurationService configurationService: IConfigurationService,
@IContextKeyService contextKeyService: IContextKeyService,
@ICommandService private readonly commandService: ICommandService,
@IDebugService private readonly debugService: IDebugService,
@IEditorService private readonly editorService: IEditorService,
@IWorkspaceContextService private readonly workspaceContextService: IWorkspaceContextService,
@IFileDialogService private readonly dialogService: IFileDialogService,
@IInstantiationService instantiationService: IInstantiationService,
@IViewDescriptorService viewDescriptorService: IViewDescriptorService,
@ITelemetryService private readonly telemetryService: ITelemetryService,
@IOpenerService openerService: IOpenerService,
) {
super({ ...(options as IViewPaneOptions), ariaHeaderLabel: localize('debugStart', "Debug Start Section") }, keybindingService, contextMenuService, configurationService, contextKeyService, viewDescriptorService, instantiationService, openerService, themeService);
this._register(editorService.onDidActiveEditorChange(() => this.updateView()));
this._register(this.debugService.getConfigurationManager().onDidRegisterDebugger(() => this.updateView()));
this.debuggerInterestedContext = CONTEXT_DEBUGGER_INTERESTED.bindTo(contextKeyService);
const setContextKey = () => {
const activeEditor = this.editorService.activeTextEditorWidget;
const debuggerLabels = this.debugService.getConfigurationManager().getDebuggerLabelsForEditor(activeEditor);
this.debuggerInterestedContext.set(debuggerLabels.length > 0);
};
this._register(editorService.onDidActiveEditorChange(setContextKey));
this._register(this.debugService.getConfigurationManager().onDidRegisterDebugger(setContextKey));
}
private updateView(): void {
const activeEditor = this.editorService.activeTextEditorWidget;
const debuggerLabels = this.debugService.getConfigurationManager().getDebuggerLabelsForEditor(activeEditor);
if (!equals(this.debuggerLabels, debuggerLabels)) {
this.debuggerLabels = debuggerLabels;
const enabled = this.debuggerLabels.length > 0;
this.debugButton.enabled = enabled;
const debugKeybinding = this.keybindingService.lookupKeybinding(StartAction.ID);
let debugLabel = this.debuggerLabels.length !== 1 ? localize('debug', "Run and Debug") : localize('debugWith', "Run and Debug {0}", this.debuggerLabels[0]);
if (debugKeybinding) {
debugLabel += ` (${debugKeybinding.getLabel()})`;
}
this.debugButton.label = debugLabel;
const emptyWorkbench = this.workspaceContextService.getWorkbenchState() === WorkbenchState.EMPTY;
this.firstMessageContainer.innerHTML = '';
this.secondMessageContainer.innerHTML = '';
const secondMessageElement = $('span');
this.secondMessageContainer.appendChild(secondMessageElement);
const setSecondMessage = () => {
secondMessageElement.textContent = localize('specifyHowToRun', "To customize Run and Debug");
this.clickElement = createClickElement(localize('configure', " create a launch.json file."), () => {
this.telemetryService.publicLog2<DebugStartMetrics, DebugStartMetricsClassification>('debugStart.configure', { debuggers: this.debuggerLabels });
this.commandService.executeCommand(ConfigureAction.ID);
});
this.secondMessageContainer.appendChild(this.clickElement);
};
const setSecondMessageWithFolder = () => {
secondMessageElement.textContent = localize('noLaunchConfiguration', "To customize Run and Debug, ");
this.clickElement = createClickElement(localize('openFolder', " open a folder"), () => {
this.telemetryService.publicLog2<DebugStartMetrics, DebugStartMetricsClassification>('debugStart.openFolder', { debuggers: this.debuggerLabels });
this.dialogService.pickFolderAndOpen({ forceNewWindow: false });
});
this.secondMessageContainer.appendChild(this.clickElement);
const moreText = $('span.moreText');
moreText.textContent = localize('andconfigure', " and create a launch.json file.");
this.secondMessageContainer.appendChild(moreText);
};
if (enabled && !emptyWorkbench) {
setSecondMessage();
}
if (enabled && emptyWorkbench) {
setSecondMessageWithFolder();
}
if (!enabled && !emptyWorkbench) {
const firstMessageElement = $('span');
this.firstMessageContainer.appendChild(firstMessageElement);
firstMessageElement.textContent = localize('simplyDebugAndRun', "Open a file which can be debugged or run.");
setSecondMessage();
}
if (!enabled && emptyWorkbench) {
this.clickElement = createClickElement(localize('openFile', "Open a file"), () => {
this.telemetryService.publicLog2<DebugStartMetrics, DebugStartMetricsClassification>('debugStart.openFile');
this.dialogService.pickFileAndOpen({ forceNewWindow: false });
});
this.firstMessageContainer.appendChild(this.clickElement);
const firstMessageElement = $('span');
this.firstMessageContainer.appendChild(firstMessageElement);
firstMessageElement.textContent = localize('canBeDebuggedOrRun', " which can be debugged or run.");
setSecondMessageWithFolder();
}
}
}
protected renderBody(container: HTMLElement): void {
super.renderBody(container);
this.firstMessageContainer = $('.top-section');
container.appendChild(this.firstMessageContainer);
this.debugButton = new Button(container);
this._register(this.debugButton.onDidClick(() => {
this.commandService.executeCommand(StartAction.ID);
this.telemetryService.publicLog2<DebugStartMetrics, DebugStartMetricsClassification>('debugStart.runAndDebug', { debuggers: this.debuggerLabels });
}));
attachButtonStyler(this.debugButton, this.themeService);
dom.addClass(this.element, 'debug-pane');
dom.addClass(container, 'debug-start-view');
this.secondMessageContainer = $('.section');
container.appendChild(this.secondMessageContainer);
this.updateView();
}
protected layoutBody(_: number, __: number): void {
// no-op
}
focus(): void {
if (this.debugButton.enabled) {
this.debugButton.focus();
} else if (this.clickElement) {
this.clickElement.focus();
}
shouldShowWelcome(): boolean {
return true;
}
}
const viewsRegistry = Registry.as<IViewsRegistry>(Extensions.ViewsRegistry);
viewsRegistry.registerViewWelcomeContent(StartView.ID, {
content: localize('openAFileWhichCanBeDebugged', "[Open a file](command:{0}) which can be debugged or run.", isMacintosh ? OpenFileFolderAction.ID : OpenFileAction.ID),
when: CONTEXT_DEBUGGER_INTERESTED.toNegated()
});
viewsRegistry.registerViewWelcomeContent(StartView.ID, {
content: localize('runAndDebugAction', "[Run and Debug](command:{0})", StartAction.ID)
});
viewsRegistry.registerViewWelcomeContent(StartView.ID, {
content: localize('customizeRunAndDebug', "To customize Run and Debug [create a launch.json file](command:{0}).", ConfigureAction.ID),
when: WorkbenchStateContext.notEqualsTo('empty')
});
viewsRegistry.registerViewWelcomeContent(StartView.ID, {
content: localize('customizeRunAndDebugOpenFolder', "To customize Run and Debug, [open a folder](command:{0}) and create a launch.json file.", isMacintosh ? OpenFileFolderAction.ID : OpenFolderAction.ID),
when: WorkbenchStateContext.isEqualTo('empty')
});

View File

@@ -1773,6 +1773,16 @@ declare module DebugProtocol {
If missing the value 0 is assumed which results in the completion text being inserted.
*/
length?: number;
/** Determines the start of the new selection after the text has been inserted (or replaced).
The start position must in the range 0 and length of the completion text.
If omitted the selection starts at the end of the completion text.
*/
selectionStart?: number;
/** Determines the length of the new selection after the text has been inserted (or replaced).
The selection can not extend beyond the bounds of the completion text.
If omitted the length is assumed to be 0.
*/
selectionLength?: number;
}
/** Some predefined types for the CompletionItem. Please note that not all clients have specific icons for all of them. */

View File

@@ -14,7 +14,7 @@ import { IWorkbenchActionRegistry, Extensions as WorkbenchActionExtensions } fro
import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions, IWorkbenchContribution } from 'vs/workbench/common/contributions';
import { IOutputChannelRegistry, Extensions as OutputExtensions } from 'vs/workbench/services/output/common/output';
import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors';
import { VIEWLET_ID, IExtensionsWorkbenchService, IExtensionsViewPaneContainer } from 'vs/workbench/contrib/extensions/common/extensions';
import { VIEWLET_ID, IExtensionsWorkbenchService, IExtensionsViewPaneContainer, TOGGLE_IGNORE_EXTENSION_ACTION_ID } from 'vs/workbench/contrib/extensions/common/extensions';
import { ExtensionsWorkbenchService } from 'vs/workbench/contrib/extensions/browser/extensionsWorkbenchService';
import {
OpenExtensionsViewletAction, InstallExtensionsAction, ShowOutdatedExtensionsAction, ShowRecommendedExtensionsAction, ShowRecommendedKeymapExtensionsAction, ShowPopularExtensionsAction,
@@ -48,6 +48,8 @@ import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService
import { IPreferencesService } from 'vs/workbench/services/preferences/common/preferences';
import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet';
import { IConfigurationService, ConfigurationTarget } from 'vs/platform/configuration/common/configuration';
import { CONTEXT_SYNC_ENABLEMENT } from 'vs/platform/userDataSync/common/userDataSync';
// Singletons
registerSingleton(IExtensionsWorkbenchService, ExtensionsWorkbenchService);
@@ -443,6 +445,33 @@ registerAction2(class extends Action2 {
}
});
registerAction2(class extends Action2 {
constructor() {
super({
id: TOGGLE_IGNORE_EXTENSION_ACTION_ID,
title: { value: localize('workbench.extensions.action.toggleIgnoreExtension', "Don't Sync This Extension"), original: `Don't Sync This Extension` },
menu: {
id: MenuId.ExtensionContext,
group: '2_configure',
when: CONTEXT_SYNC_ENABLEMENT
},
});
}
async run(accessor: ServicesAccessor, id: string) {
const configurationService = accessor.get(IConfigurationService);
const ignoredExtensions = [...configurationService.getValue<string[]>('sync.ignoredExtensions')];
const index = ignoredExtensions.findIndex(ignoredExtension => areSameExtensions({ id: ignoredExtension }, { id }));
if (index !== -1) {
ignoredExtensions.splice(index, 1);
} else {
ignoredExtensions.push(id);
}
return configurationService.updateValue('sync.ignoredExtensions', ignoredExtensions.length ? ignoredExtensions : undefined, ConfigurationTarget.USER);
}
});
const workbenchRegistry = Registry.as<IWorkbenchContributionsRegistry>(WorkbenchExtensions.Workbench);
class ExtensionsContributions implements IWorkbenchContribution {

View File

@@ -13,7 +13,7 @@ import * as json from 'vs/base/common/json';
import { ActionViewItem, Separator, IActionViewItemOptions } from 'vs/base/browser/ui/actionbar/actionbar';
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
import { dispose, Disposable } from 'vs/base/common/lifecycle';
import { IExtension, ExtensionState, IExtensionsWorkbenchService, VIEWLET_ID, IExtensionsViewPaneContainer, AutoUpdateConfigurationKey, IExtensionContainer, EXTENSIONS_CONFIG } from 'vs/workbench/contrib/extensions/common/extensions';
import { IExtension, ExtensionState, IExtensionsWorkbenchService, VIEWLET_ID, IExtensionsViewPaneContainer, AutoUpdateConfigurationKey, IExtensionContainer, EXTENSIONS_CONFIG, TOGGLE_IGNORE_EXTENSION_ACTION_ID } from 'vs/workbench/contrib/extensions/common/extensions';
import { ExtensionsConfigurationInitialContent } from 'vs/workbench/contrib/extensions/common/extensionsFileTemplate';
import { ExtensionsLabel, IGalleryExtension, IExtensionGalleryService, INSTALL_ERROR_MALICIOUS, INSTALL_ERROR_INCOMPATIBLE, IGalleryExtensionVersion, ILocalExtension, INSTALL_ERROR_NOT_SUPPORTED } from 'vs/platform/extensionManagement/common/extensionManagement';
import { IWorkbenchExtensionEnablementService, EnablementState, IExtensionManagementServerService, IExtensionTipsService, IExtensionRecommendation, IExtensionsConfigContent, IExtensionManagementServer } from 'vs/workbench/services/extensionManagement/common/extensionManagement';
@@ -679,7 +679,7 @@ export class DropDownMenuActionViewItem extends ExtensionActionViewItem {
}
}
export function getContextMenuActions(menuService: IMenuService, contextKeyService: IContextKeyService, extension: IExtension | undefined | null): ExtensionAction[][] {
export function getContextMenuActions(menuService: IMenuService, contextKeyService: IContextKeyService, instantiationService: IInstantiationService, extension: IExtension | undefined | null): ExtensionAction[][] {
const scopedContextKeyService = contextKeyService.createScoped();
if (extension) {
scopedContextKeyService.createKey<boolean>('isBuiltinExtension', extension.type === ExtensionType.System);
@@ -691,7 +691,7 @@ export function getContextMenuActions(menuService: IMenuService, contextKeyServi
const groups: ExtensionAction[][] = [];
const menu = menuService.createMenu(MenuId.ExtensionContext, scopedContextKeyService);
menu.getActions({ shouldForwardArgs: true }).forEach(([, actions]) => groups.push(actions.map(action => new MenuItemExtensionAction(action))));
menu.getActions({ shouldForwardArgs: true }).forEach(([, actions]) => groups.push(actions.map(action => instantiationService.createInstance(MenuItemExtensionAction, action))));
menu.dispose();
return groups;
@@ -745,7 +745,7 @@ export class ManageExtensionAction extends ExtensionDropDownAction {
groups.push([this.instantiationService.createInstance(UninstallAction)]);
groups.push([this.instantiationService.createInstance(InstallAnotherVersionAction)]);
getContextMenuActions(this.menuService, this.contextKeyService, this.extension).forEach(actions => groups.push(actions));
getContextMenuActions(this.menuService, this.contextKeyService, this.instantiationService, this.extension).forEach(actions => groups.push(actions));
groups.forEach(group => group.forEach(extensionAction => extensionAction.extension = this.extension));
@@ -773,11 +773,21 @@ export class ManageExtensionAction extends ExtensionDropDownAction {
export class MenuItemExtensionAction extends ExtensionAction {
constructor(private readonly action: IAction) {
constructor(
private readonly action: IAction,
@IConfigurationService private readonly configurationService: IConfigurationService
) {
super(action.id, action.label);
}
update() { }
update() {
if (!this.extension) {
return;
}
if (this.action.id === TOGGLE_IGNORE_EXTENSION_ACTION_ID) {
this.checked = this.configurationService.getValue<string[]>('sync.ignoredExtensions').some(id => areSameExtensions({ id }, this.extension!.identifier));
}
}
async run(): Promise<void> {
if (this.extension) {

View File

@@ -247,7 +247,7 @@ export class ExtensionsListView extends ViewPane {
getActions: () => actions.slice(0, actions.length - 1)
});
} else if (e.element) {
const groups = getContextMenuActions(this.menuService, this.contextKeyService.createScoped(), e.element);
const groups = getContextMenuActions(this.menuService, this.contextKeyService.createScoped(), this.instantiationService, e.element);
groups.forEach(group => group.forEach(extensionAction => extensionAction.extension = e.element!));
let actions: IAction[] = [];
for (const menuActions of groups) {

View File

@@ -143,3 +143,5 @@ export class ExtensionContainers extends Disposable {
}
}
}
export const TOGGLE_IGNORE_EXTENSION_ACTION_ID = 'workbench.extensions.action.toggleIgnoreExtension';

View File

@@ -13,13 +13,15 @@ export class ExtensionsInput extends EditorInput {
static readonly ID = 'workbench.extensions.input2';
get extension(): IExtension { return this._extension; }
readonly resource = URI.from({
scheme: 'extension',
path: this.extension.identifier.id
});
get resource() {
return URI.from({
scheme: 'extension',
path: this.extension.identifier.id
});
}
constructor(
private _extension: IExtension,
private readonly _extension: IExtension
) {
super();
}

View File

@@ -16,10 +16,6 @@ export class RuntimeExtensionsInput extends EditorInput {
path: 'default'
});
constructor() {
super();
}
getTypeId(): string {
return RuntimeExtensionsInput.ID;
}

View File

@@ -7,7 +7,7 @@ import { IWorkbenchContribution } from 'vs/workbench/common/contributions';
import { URI } from 'vs/base/common/uri';
import { IEditorViewState } from 'vs/editor/common/editorCommon';
import { toResource, SideBySideEditorInput, IWorkbenchEditorConfiguration, SideBySideEditor as SideBySideEditorChoice } from 'vs/workbench/common/editor';
import { ITextFileService, ModelState } from 'vs/workbench/services/textfile/common/textfiles';
import { ITextFileService, TextFileEditorModelState } from 'vs/workbench/services/textfile/common/textfiles';
import { FileOperationEvent, FileOperation, IFileService, FileChangeType, FileChangesEvent, FileSystemProviderCapabilities } from 'vs/platform/files/common/files';
import { FileEditorInput } from 'vs/workbench/contrib/files/common/editors/fileEditorInput';
import { ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle';
@@ -62,9 +62,9 @@ export class FileEditorTracker extends Disposable implements IWorkbenchContribut
this._register(this.fileService.onDidFilesChange(e => this.onDidFilesChange(e)));
// Ensure dirty text file and untitled models are always opened as editors
this._register(this.textFileService.files.onDidChangeDirty(m => this.ensureDirtyFilesAreOpenedWorker.work(m.resource)));
this._register(this.textFileService.files.onDidSaveError(m => this.ensureDirtyFilesAreOpenedWorker.work(m.resource)));
this._register(this.textFileService.untitled.onDidChangeDirty(r => this.ensureDirtyFilesAreOpenedWorker.work(r)));
this._register(this.textFileService.files.onDidChangeDirty(model => this.ensureDirtyFilesAreOpenedWorker.work(model.resource)));
this._register(this.textFileService.files.onDidSaveError(model => this.ensureDirtyFilesAreOpenedWorker.work(model.resource)));
this._register(this.textFileService.untitled.onDidChangeDirty(model => this.ensureDirtyFilesAreOpenedWorker.work(model.resource)));
// Out of workspace file watchers
this._register(this.editorService.onDidVisibleEditorsChange(() => this.onDidVisibleEditorsChange()));
@@ -290,7 +290,7 @@ export class FileEditorTracker extends Disposable implements IWorkbenchContribut
}
const model = this.textFileService.files.get(resource);
if (model?.hasState(ModelState.PENDING_SAVE)) {
if (model?.hasState(TextFileEditorModelState.PENDING_SAVE)) {
return false; // resource must not be pending to save
}
@@ -369,18 +369,14 @@ export class FileEditorTracker extends Disposable implements IWorkbenchContribut
}
const model = this.textFileService.files.get(resource);
if (!model) {
return undefined;
}
if (model.isDirty()) {
if (!model || model.isDirty() || !model.isResolved()) {
return undefined;
}
return model;
})),
model => model.resource.toString()
).forEach(model => model.load());
).forEach(model => this.textFileService.files.resolve(model.resource, { reload: { async: true } }));
}
}

View File

@@ -10,7 +10,7 @@ import { isValidBasename } from 'vs/base/common/extpath';
import { basename } from 'vs/base/common/resources';
import { Action } from 'vs/base/common/actions';
import { VIEWLET_ID, TEXT_FILE_EDITOR_ID, IExplorerService } from 'vs/workbench/contrib/files/common/files';
import { ITextFileEditorModel, ITextFileService, TextFileOperationError, TextFileOperationResult } from 'vs/workbench/services/textfile/common/textfiles';
import { ITextFileService, TextFileOperationError, TextFileOperationResult } from 'vs/workbench/services/textfile/common/textfiles';
import { BaseTextEditor, IEditorConfiguration } from 'vs/workbench/browser/parts/editor/textEditor';
import { EditorOptions, TextEditorOptions, IEditorCloseEvent } from 'vs/workbench/common/editor';
import { BinaryEditorModel } from 'vs/workbench/common/editor/binaryEditorModel';
@@ -135,7 +135,7 @@ export class TextFileEditor extends BaseTextEditor {
return this.openAsBinary(input, options);
}
const textFileModel = <ITextFileEditorModel>resolvedModel;
const textFileModel = resolvedModel;
// Editor
const textEditor = assertIsDefined(this.getControl());

View File

@@ -221,13 +221,11 @@ class DoNotShowResolveConflictLearnMoreAction extends Action {
super('workbench.files.action.resolveConflictLearnMoreDoNotShowAgain', nls.localize('dontShowAgain', "Don't Show Again"));
}
run(notification: IDisposable): Promise<any> {
async run(notification: IDisposable): Promise<any> {
this.storageService.store(LEARN_MORE_DIRTY_WRITE_IGNORE_KEY, true, StorageScope.GLOBAL);
// Hide notification
notification.dispose();
return Promise.resolve();
}
}
@@ -262,8 +260,6 @@ class ResolveSaveConflictAction extends Action {
Event.once(handle.onDidClose)(() => dispose(actions.primary!));
pendingResolveSaveConflictMessages.push(handle);
}
return Promise.resolve(true);
}
}
@@ -276,7 +272,7 @@ class SaveElevatedAction extends Action {
super('workbench.files.action.saveElevated', triedToMakeWriteable ? isWindows ? nls.localize('overwriteElevated', "Overwrite as Admin...") : nls.localize('overwriteElevatedSudo', "Overwrite as Sudo...") : isWindows ? nls.localize('saveElevated', "Retry as Admin...") : nls.localize('saveElevatedSudo', "Retry as Sudo..."));
}
run(): Promise<any> {
async run(): Promise<any> {
if (!this.model.isDisposed()) {
this.model.save({
writeElevated: true,
@@ -284,8 +280,6 @@ class SaveElevatedAction extends Action {
reason: SaveReason.EXPLICIT
});
}
return Promise.resolve(true);
}
}
@@ -297,12 +291,10 @@ class OverwriteReadonlyAction extends Action {
super('workbench.files.action.overwrite', nls.localize('overwrite', "Overwrite"));
}
run(): Promise<any> {
async run(): Promise<any> {
if (!this.model.isDisposed()) {
this.model.save({ overwriteReadonly: true, reason: SaveReason.EXPLICIT });
}
return Promise.resolve(true);
}
}
@@ -314,12 +306,10 @@ class SaveIgnoreModifiedSinceAction extends Action {
super('workbench.files.action.saveIgnoreModifiedSince', nls.localize('overwrite', "Overwrite"));
}
run(): Promise<any> {
async run(): Promise<any> {
if (!this.model.isDisposed()) {
this.model.save({ ignoreModifiedSince: true, reason: SaveReason.EXPLICIT });
}
return Promise.resolve(true);
}
}
@@ -331,10 +321,8 @@ class ConfigureSaveConflictAction extends Action {
super('workbench.files.action.configureSaveConflict', nls.localize('configure', "Configure"));
}
run(): Promise<any> {
async run(): Promise<any> {
this.preferencesService.openSettings(undefined, 'files.saveConflictResolution');
return Promise.resolve(true);
}
}

View File

@@ -18,7 +18,7 @@ import { IExtensionService } from 'vs/workbench/services/extensions/common/exten
import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection';
import { IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey';
import { IContextKeyService, IContextKey, ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
import { IThemeService } from 'vs/platform/theme/common/themeService';
import { IViewsRegistry, IViewDescriptor, Extensions, ViewContainer, IViewContainersRegistry, ViewContainerLocation, IViewDescriptorService } from 'vs/workbench/common/views';
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
@@ -34,6 +34,9 @@ import { KeyChord, KeyMod, KeyCode } from 'vs/base/common/keyCodes';
import { Registry } from 'vs/platform/registry/common/platform';
import { IProgressService, ProgressLocation } from 'vs/platform/progress/common/progress';
import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors';
import { WorkbenchStateContext, RemoteNameContext, IsWebContext } from 'vs/workbench/browser/contextkeys';
import { AddRootFolderAction, OpenFolderAction, OpenFileFolderAction } from 'vs/workbench/browser/actions/workspaceActions';
import { isMacintosh } from 'vs/base/common/platform';
export class ExplorerViewletViewsContribution extends Disposable implements IWorkbenchContribution {
@@ -61,6 +64,23 @@ export class ExplorerViewletViewsContribution extends Disposable implements IWor
private registerViews(): void {
const viewsRegistry = Registry.as<IViewsRegistry>(Extensions.ViewsRegistry);
viewsRegistry.registerViewWelcomeContent(EmptyView.ID, {
content: localize('noWorkspaceHelp', "You have not yet added a folder to the workspace.\n[Add Folder](command:{0})", AddRootFolderAction.ID),
when: WorkbenchStateContext.isEqualTo('workspace')
});
const commandId = isMacintosh ? OpenFileFolderAction.ID : OpenFolderAction.ID;
viewsRegistry.registerViewWelcomeContent(EmptyView.ID, {
content: localize('remoteNoFolderHelp', "Connected to remote.\n[Open Folder](command:{0})", commandId),
when: ContextKeyExpr.and(WorkbenchStateContext.notEqualsTo('workspace'), RemoteNameContext.notEqualsTo(''), IsWebContext.toNegated())
});
viewsRegistry.registerViewWelcomeContent(EmptyView.ID, {
content: localize('noFolderHelp', "You have not yet opened a folder.\n[Open Folder](command:{0})", commandId),
when: ContextKeyExpr.or(ContextKeyExpr.and(WorkbenchStateContext.notEqualsTo('workspace'), RemoteNameContext.isEqualTo('')), ContextKeyExpr.and(WorkbenchStateContext.notEqualsTo('workspace'), IsWebContext))
});
const viewDescriptors = viewsRegistry.getViews(VIEW_CONTAINER);
let viewDescriptorsToRegister: IViewDescriptor[] = [];

View File

@@ -166,7 +166,7 @@ export class GlobalNewUntitledFileAction extends Action {
}
}
async function deleteFiles(workingCopyService: IWorkingCopyService, workingCopyFileService: IWorkingCopyFileService, dialogService: IDialogService, configurationService: IConfigurationService, elements: ExplorerItem[], useTrash: boolean, skipConfirm = false): Promise<void> {
async function deleteFiles(workingCopyFileService: IWorkingCopyFileService, dialogService: IDialogService, configurationService: IConfigurationService, elements: ExplorerItem[], useTrash: boolean, skipConfirm = false): Promise<void> {
let primaryButton: string;
if (useTrash) {
primaryButton = isWindows ? nls.localize('deleteButtonLabelRecycleBin', "&&Move to Recycle Bin") : nls.localize({ key: 'deleteButtonLabelTrash', comment: ['&& denotes a mnemonic'] }, "&&Move to Trash");
@@ -174,20 +174,24 @@ async function deleteFiles(workingCopyService: IWorkingCopyService, workingCopyF
primaryButton = nls.localize({ key: 'deleteButtonLabel', comment: ['&& denotes a mnemonic'] }, "&&Delete");
}
const distinctElements = resources.distinctParents(elements, e => e.resource);
// Handle dirty
const distinctElements = resources.distinctParents(elements, e => e.resource);
const dirtyWorkingCopies = new Set<IWorkingCopy>();
for (const distinctElement of distinctElements) {
for (const dirtyWorkingCopy of workingCopyFileService.getDirty(distinctElement.resource)) {
dirtyWorkingCopies.add(dirtyWorkingCopy);
}
}
let confirmed = true;
const dirtyWorkingCopies = workingCopyService.dirtyWorkingCopies.filter(workingCopy => distinctElements.some(e => resources.isEqualOrParent(workingCopy.resource, e.resource)));
if (dirtyWorkingCopies.length) {
if (dirtyWorkingCopies.size) {
let message: string;
if (distinctElements.length > 1) {
message = nls.localize('dirtyMessageFilesDelete', "You are deleting files with unsaved changes. Do you want to continue?");
} else if (distinctElements[0].isDirectory) {
if (dirtyWorkingCopies.length === 1) {
if (dirtyWorkingCopies.size === 1) {
message = nls.localize('dirtyMessageFolderOneDelete', "You are deleting a folder {0} with unsaved changes in 1 file. Do you want to continue?", distinctElements[0].name);
} else {
message = nls.localize('dirtyMessageFolderDelete', "You are deleting a folder {0} with unsaved changes in {1} files. Do you want to continue?", distinctElements[0].name, dirtyWorkingCopies.length);
message = nls.localize('dirtyMessageFolderDelete', "You are deleting a folder {0} with unsaved changes in {1} files. Do you want to continue?", distinctElements[0].name, dirtyWorkingCopies.size);
}
} else {
message = nls.localize('dirtyMessageFileDelete', "You are deleting {0} with unsaved changes. Do you want to continue?", distinctElements[0].name);
@@ -204,7 +208,6 @@ async function deleteFiles(workingCopyService: IWorkingCopyService, workingCopyF
confirmed = false;
} else {
skipConfirm = true;
await Promise.all(dirtyWorkingCopies.map(dirty => dirty.revert()));
}
}
@@ -296,7 +299,7 @@ async function deleteFiles(workingCopyService: IWorkingCopyService, workingCopyF
skipConfirm = true;
return deleteFiles(workingCopyService, workingCopyFileService, dialogService, configurationService, elements, useTrash, skipConfirm);
return deleteFiles(workingCopyFileService, dialogService, configurationService, elements, useTrash, skipConfirm);
}
}
}
@@ -989,7 +992,7 @@ export const moveFileToTrashHandler = async (accessor: ServicesAccessor) => {
const explorerService = accessor.get(IExplorerService);
const stats = explorerService.getContext(true).filter(s => !s.isRoot);
if (stats.length) {
await deleteFiles(accessor.get(IWorkingCopyService), accessor.get(IWorkingCopyFileService), accessor.get(IDialogService), accessor.get(IConfigurationService), stats, true);
await deleteFiles(accessor.get(IWorkingCopyFileService), accessor.get(IDialogService), accessor.get(IConfigurationService), stats, true);
}
};
@@ -998,7 +1001,7 @@ export const deleteFileHandler = async (accessor: ServicesAccessor) => {
const stats = explorerService.getContext(true).filter(s => !s.isRoot);
if (stats.length) {
await deleteFiles(accessor.get(IWorkingCopyService), accessor.get(IWorkingCopyFileService), accessor.get(IDialogService), accessor.get(IConfigurationService), stats, false);
await deleteFiles(accessor.get(IWorkingCopyFileService), accessor.get(IDialogService), accessor.get(IConfigurationService), stats, false);
}
};

View File

@@ -66,17 +66,6 @@
border-radius: 0; /* goes better when ellipsis shows up on narrow sidebar */
}
.explorer-viewlet .explorer-empty-view {
padding: 0 20px 0 20px;
}
.explorer-viewlet .explorer-empty-view .monaco-button {
max-width: 260px;
margin-left: auto;
margin-right: auto;
display: block;
}
.explorer-viewlet .explorer-item.nonexistent-root {
opacity: 0.5;
}

View File

@@ -4,13 +4,8 @@
*--------------------------------------------------------------------------------------------*/
import * as nls from 'vs/nls';
import * as errors from 'vs/base/common/errors';
import * as DOM from 'vs/base/browser/dom';
import { Button } from 'vs/base/browser/ui/button/button';
import { IViewletViewOptions } from 'vs/workbench/browser/parts/views/viewsViewlet';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { OpenFolderAction, AddRootFolderAction } from 'vs/workbench/browser/actions/workspaceActions';
import { attachButtonStyler } from 'vs/platform/theme/common/styler';
import { IThemeService } from 'vs/platform/theme/common/themeService';
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
@@ -20,10 +15,7 @@ import { ViewPane, IViewPaneOptions } from 'vs/workbench/browser/parts/views/vie
import { ResourcesDropHandler, DragAndDropObserver } from 'vs/workbench/browser/dnd';
import { listDropBackground } from 'vs/platform/theme/common/colorRegistry';
import { SIDE_BAR_BACKGROUND } from 'vs/workbench/common/theme';
import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
import { ILabelService } from 'vs/platform/label/common/label';
import { Schemas } from 'vs/base/common/network';
import { isWeb } from 'vs/base/common/platform';
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
import { IViewDescriptorService } from 'vs/workbench/common/views';
import { IOpenerService } from 'vs/platform/opener/common/opener';
@@ -33,9 +25,6 @@ export class EmptyView extends ViewPane {
static readonly ID: string = 'workbench.explorer.emptyView';
static readonly NAME = nls.localize('noWorkspace', "No Folder Opened");
private button!: Button;
private messageElement!: HTMLElement;
constructor(
options: IViewletViewOptions,
@IThemeService themeService: IThemeService,
@@ -45,55 +34,31 @@ export class EmptyView extends ViewPane {
@IContextMenuService contextMenuService: IContextMenuService,
@IWorkspaceContextService private readonly contextService: IWorkspaceContextService,
@IConfigurationService configurationService: IConfigurationService,
@IWorkbenchEnvironmentService private environmentService: IWorkbenchEnvironmentService,
@ILabelService private labelService: ILabelService,
@IContextKeyService contextKeyService: IContextKeyService,
@IOpenerService openerService: IOpenerService
) {
super({ ...(options as IViewPaneOptions), ariaHeaderLabel: nls.localize('explorerSection', "Explorer Section: No Folder Opened") }, keybindingService, contextMenuService, configurationService, contextKeyService, viewDescriptorService, instantiationService, openerService, themeService);
this._register(this.contextService.onDidChangeWorkbenchState(() => this.setLabels()));
this._register(this.labelService.onDidChangeFormatters(() => this.setLabels()));
this._register(this.contextService.onDidChangeWorkbenchState(() => this.refreshTitle()));
this._register(this.labelService.onDidChangeFormatters(() => this.refreshTitle()));
}
shouldShowWelcome(): boolean {
return true;
}
protected renderBody(container: HTMLElement): void {
super.renderBody(container);
DOM.addClass(container, 'explorer-empty-view');
container.tabIndex = 0;
const messageContainer = document.createElement('div');
DOM.addClass(messageContainer, 'section');
container.appendChild(messageContainer);
this.messageElement = document.createElement('p');
messageContainer.appendChild(this.messageElement);
this.button = new Button(messageContainer);
attachButtonStyler(this.button, this.themeService);
this._register(this.button.onDidClick(() => {
if (!this.actionRunner) {
return;
}
const action = this.contextService.getWorkbenchState() === WorkbenchState.WORKSPACE
? this.instantiationService.createInstance(AddRootFolderAction, AddRootFolderAction.ID, AddRootFolderAction.LABEL)
: this.instantiationService.createInstance(OpenFolderAction, OpenFolderAction.ID, OpenFolderAction.LABEL);
this.actionRunner.run(action).then(() => {
action.dispose();
}, err => {
action.dispose();
errors.onUnexpectedError(err);
});
}));
this._register(new DragAndDropObserver(container, {
onDrop: e => {
const color = this.themeService.getTheme().getColor(SIDE_BAR_BACKGROUND);
container.style.backgroundColor = color ? color.toString() : '';
const dropHandler = this.instantiationService.createInstance(ResourcesDropHandler, { allowWorkspaceOpen: true });
dropHandler.handleDrop(e, () => undefined, targetGroup => undefined);
dropHandler.handleDrop(e, () => undefined, () => undefined);
},
onDragEnter: (e) => {
onDragEnter: () => {
const color = this.themeService.getTheme().getColor(listDropBackground);
container.style.backgroundColor = color ? color.toString() : '';
},
@@ -112,26 +77,13 @@ export class EmptyView extends ViewPane {
}
}));
this.setLabels();
this.refreshTitle();
}
private setLabels(): void {
private refreshTitle(): void {
if (this.contextService.getWorkbenchState() === WorkbenchState.WORKSPACE) {
this.messageElement.textContent = nls.localize('noWorkspaceHelp', "You have not yet added a folder to the workspace.");
if (this.button) {
this.button.label = nls.localize('addFolder', "Add Folder");
}
this.updateTitle(EmptyView.NAME);
} else {
if (this.environmentService.configuration.remoteAuthority && !isWeb) {
const hostLabel = this.labelService.getHostLabel(Schemas.vscodeRemote, this.environmentService.configuration.remoteAuthority);
this.messageElement.textContent = hostLabel ? nls.localize('remoteNoFolderHelp', "Connected to {0}", hostLabel) : nls.localize('connecting', "Connecting...");
} else {
this.messageElement.textContent = nls.localize('noFolderHelp', "You have not yet opened a folder.");
}
if (this.button) {
this.button.label = nls.localize('openFolder', "Open Folder");
}
this.updateTitle(this.title);
}
}
@@ -139,9 +91,4 @@ export class EmptyView extends ViewPane {
layoutBody(_size: number): void {
// no-op
}
focus(): void {
this.button.element.focus();
}
}

View File

@@ -55,7 +55,6 @@ import { ILabelService } from 'vs/platform/label/common/label';
import { isNumber } from 'vs/base/common/types';
import { domEvent } from 'vs/base/browser/event';
import { IEditableData } from 'vs/workbench/common/views';
import { IWorkingCopyService } from 'vs/workbench/services/workingCopy/common/workingCopyService';
export class ExplorerDelegate implements IListVirtualDelegate<ExplorerItem> {
@@ -643,8 +642,7 @@ export class FileDragAndDrop implements ITreeDragAndDrop<ExplorerItem> {
@IInstantiationService private instantiationService: IInstantiationService,
@IWorkingCopyFileService private workingCopyFileService: IWorkingCopyFileService,
@IHostService private hostService: IHostService,
@IWorkspaceEditingService private workspaceEditingService: IWorkspaceEditingService,
@IWorkingCopyService private workingCopyService: IWorkingCopyService
@IWorkspaceEditingService private workspaceEditingService: IWorkspaceEditingService
) {
this.toDispose = [];
@@ -945,15 +943,7 @@ export class FileDragAndDrop implements ITreeDragAndDrop<ExplorerItem> {
const sourceFile = resource;
const targetFile = joinPath(target.resource, basename(sourceFile));
// if the target exists and is dirty, make sure to revert it. otherwise the dirty contents
// of the target file would replace the contents of the added file. since we already
// confirmed the overwrite before, this is OK.
if (this.workingCopyService.isDirty(targetFile)) {
await Promise.all(this.workingCopyService.getWorkingCopies(targetFile).map(workingCopy => workingCopy.revert({ soft: true })));
}
const copyTarget = joinPath(target.resource, basename(sourceFile));
const stat = await this.workingCopyFileService.copy(sourceFile, copyTarget, true);
const stat = await this.workingCopyFileService.copy(sourceFile, targetFile, true);
// if we only add one file, just open it directly
if (resources.length === 1 && !stat.isDirectory) {
this.editorService.openEditor({ resource: stat.resource, options: { pinned: true } });

View File

@@ -8,7 +8,7 @@ import { URI } from 'vs/base/common/uri';
import { EncodingMode, IFileEditorInput, Verbosity, TextResourceEditorInput } from 'vs/workbench/common/editor';
import { BinaryEditorModel } from 'vs/workbench/common/editor/binaryEditorModel';
import { FileOperationError, FileOperationResult, IFileService } from 'vs/platform/files/common/files';
import { ITextFileService, ModelState, LoadReason, TextFileOperationError, TextFileOperationResult, ITextFileEditorModel } from 'vs/workbench/services/textfile/common/textfiles';
import { ITextFileService, TextFileEditorModelState, TextFileLoadReason, TextFileOperationError, TextFileOperationResult, ITextFileEditorModel } from 'vs/workbench/services/textfile/common/textfiles';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IReference, dispose, DisposableStore } from 'vs/base/common/lifecycle';
import { ITextModelService } from 'vs/editor/common/services/resolverService';
@@ -56,6 +56,8 @@ export class FileEditorInput extends TextResourceEditorInput implements IFileEdi
) {
super(resource, editorService, editorGroupService, textFileService, labelService, fileService, filesConfigurationService);
this.model = this.textFileService.files.get(resource);
if (preferredEncoding) {
this.setPreferredEncoding(preferredEncoding);
}
@@ -63,6 +65,11 @@ export class FileEditorInput extends TextResourceEditorInput implements IFileEdi
if (preferredMode) {
this.setPreferredMode(preferredMode);
}
// If a file model already exists, make sure to wire it in
if (this.model) {
this.registerModelListeners(this.model);
}
}
protected registerListeners(): void {
@@ -98,10 +105,10 @@ export class FileEditorInput extends TextResourceEditorInput implements IFileEdi
this.modelListeners.add(model.onDidSaveError(() => this._onDidChangeDirty.fire()));
// remove model association once it gets disposed
Event.once(model.onDispose)(() => {
this.modelListeners.add(Event.once(model.onDispose)(() => {
this.modelListeners.clear();
this.model = undefined;
});
}));
}
getEncoding(): string | undefined {
@@ -167,7 +174,7 @@ export class FileEditorInput extends TextResourceEditorInput implements IFileEdi
}
private decorateLabel(label: string): string {
const orphaned = this.model?.hasState(ModelState.ORPHAN);
const orphaned = this.model?.hasState(TextFileEditorModelState.ORPHAN);
const readonly = this.isReadonly();
if (orphaned && readonly) {
@@ -198,7 +205,7 @@ export class FileEditorInput extends TextResourceEditorInput implements IFileEdi
}
isSaving(): boolean {
if (this.model?.hasState(ModelState.SAVED) || this.model?.hasState(ModelState.CONFLICT) || this.model?.hasState(ModelState.ERROR)) {
if (this.model?.hasState(TextFileEditorModelState.SAVED) || this.model?.hasState(TextFileEditorModelState.CONFLICT) || this.model?.hasState(TextFileEditorModelState.ERROR)) {
return false; // require the model to be dirty and not in conflict or error state
}
@@ -234,7 +241,7 @@ export class FileEditorInput extends TextResourceEditorInput implements IFileEdi
encoding: this.preferredEncoding,
reload: { async: true }, // trigger a reload of the model if it exists already but do not wait to show the model
allowBinary: this.forceOpenAs === ForceOpenAs.Text,
reason: LoadReason.EDITOR
reason: TextFileLoadReason.EDITOR
});
// This is a bit ugly, because we first resolve the model and then resolve a model reference. the reason being that binary

View File

@@ -28,10 +28,8 @@ export class LogViewerInput extends ResourceEditorInput {
static readonly ID = 'workbench.editorinputs.output';
readonly resource = this.outputChannelDescriptor.file;
constructor(
private readonly outputChannelDescriptor: IFileOutputChannelDescriptor,
outputChannelDescriptor: IFileOutputChannelDescriptor,
@ITextModelService textModelResolverService: ITextModelService,
@ITextFileService textFileService: ITextFileService,
@IEditorService editorService: IEditorService,

View File

@@ -13,16 +13,16 @@
}
/* Deal with overflow */
.settings-editor > .settings-body > .settings-tree-container .setting-item.setting-item-list .setting-list-widget .setting-list-value,
.settings-editor > .settings-body > .settings-tree-container .setting-item.setting-item-list .setting-list-widget .setting-list-sibling {
white-space: nowrap;
.settings-editor > .settings-body > .settings-tree-container .setting-item.setting-item-list .setting-list-value,
.settings-editor > .settings-body > .settings-tree-container .setting-item.setting-item-list .setting-list-sibling {
white-space: pre;
overflow: hidden;
text-overflow: ellipsis;
}
.settings-editor > .settings-body > .settings-tree-container .setting-item.setting-item-list .setting-list-widget .setting-list-value {
.settings-editor > .settings-body > .settings-tree-container .setting-item.setting-item-list .setting-list-value {
max-width: 90%;
}
.settings-editor > .settings-body > .settings-tree-container .setting-item.setting-item-list .setting-list-widget .setting-list-sibling {
.settings-editor > .settings-body > .settings-tree-container .setting-item.setting-item-list .setting-list-sibling {
max-width: 10%;
}

View File

@@ -1640,12 +1640,13 @@ class StopSyncingSettingAction extends Action {
}
async run(): Promise<void> {
const currentValue = this.configService.getValue<string[]>('sync.ignoredSettings');
let currentValue = [...this.configService.getValue<string[]>('sync.ignoredSettings')];
if (this.checked) {
this.configService.updateValue('sync.ignoredSettings', currentValue.filter(v => v !== this.setting.key));
currentValue = currentValue.filter(v => v !== this.setting.key);
} else {
this.configService.updateValue('sync.ignoredSettings', [...currentValue, this.setting.key]);
currentValue.push(this.setting.key);
}
this.configService.updateValue('sync.ignoredSettings', currentValue.length ? currentValue : undefined, ConfigurationTarget.USER);
return Promise.resolve(undefined);
}

View File

@@ -443,12 +443,12 @@ export class ListSettingWidget extends Disposable {
const onSubmit = (edited: boolean) => {
this.model.setEditKey('none');
const value = valueInput.value.trim();
const value = valueInput.value;
if (edited && !isUndefinedOrNull(value)) {
this._onDidChangeList.fire({
originalValue: item.value,
value: value,
sibling: siblingInput && siblingInput.value.trim(),
sibling: siblingInput && siblingInput.value,
targetIndex: idx
});
}

View File

@@ -146,13 +146,16 @@ interface ResourceTemplate {
disposables: IDisposable;
}
class MultipleSelectionActionRunner extends ActionRunner {
class RepositoryPaneActionRunner extends ActionRunner {
constructor(private getSelectedResources: () => (ISCMResource | IResourceNode<ISCMResource, ISCMResourceGroup>)[]) {
constructor(
private getSelectedResources: () => (ISCMResource | IResourceNode<ISCMResource, ISCMResourceGroup>)[],
private focus: () => void
) {
super();
}
runAction(action: IAction, context: ISCMResource | IResourceNode<ISCMResource, ISCMResourceGroup>): Promise<any> {
async runAction(action: IAction, context: ISCMResource | IResourceNode<ISCMResource, ISCMResourceGroup>): Promise<any> {
if (!(action instanceof MenuItemAction)) {
return super.runAction(action, context);
}
@@ -161,7 +164,8 @@ class MultipleSelectionActionRunner extends ActionRunner {
const contextIsSelected = selection.some(s => s === context);
const actualContext = contextIsSelected ? selection : [context];
const args = flatten(actualContext.map(e => ResourceTree.isResourceNode(e) ? ResourceTree.collect(e) : [e]));
return action.run(...args);
await action.run(...args);
this.focus();
}
}
@@ -175,6 +179,7 @@ class ResourceRenderer implements ICompressibleTreeRenderer<ISCMResource | IReso
private labels: ResourceLabels,
private actionViewItemProvider: IActionViewItemProvider,
private getSelectedResources: () => (ISCMResource | IResourceNode<ISCMResource, ISCMResourceGroup>)[],
private focus: () => void,
private themeService: IThemeService,
private menus: SCMMenus
) { }
@@ -186,7 +191,7 @@ class ResourceRenderer implements ICompressibleTreeRenderer<ISCMResource | IReso
const actionsContainer = append(fileLabel.element, $('.actions'));
const actionBar = new ActionBar(actionsContainer, {
actionViewItemProvider: this.actionViewItemProvider,
actionRunner: new MultipleSelectionActionRunner(this.getSelectedResources)
actionRunner: new RepositoryPaneActionRunner(this.getSelectedResources, this.focus)
});
const decorationIcon = append(element, $('.decoration-icon'));
@@ -730,7 +735,8 @@ export class RepositoryPane extends ViewPane {
wrappingStrategy: 'advanced',
wrappingIndent: 'none',
padding: { top: 3, bottom: 3 },
suggest: { showWords: false }
suggest: { showWords: false },
quickSuggestions: false
};
const codeEditorWidgetOptions: ICodeEditorWidgetOptions = {
@@ -820,7 +826,7 @@ export class RepositoryPane extends ViewPane {
const renderers = [
new ResourceGroupRenderer(actionViewItemProvider, this.themeService, this.menus),
new ResourceRenderer(() => this.viewModel, this.listLabels, actionViewItemProvider, () => this.getSelectedResources(), this.themeService, this.menus)
new ResourceRenderer(() => this.viewModel, this.listLabels, actionViewItemProvider, () => this.getSelectedResources(), () => this.tree.domFocus(), this.themeService, this.menus)
];
const filter = new SCMTreeFilter();
@@ -1024,7 +1030,7 @@ export class RepositoryPane extends ViewPane {
getAnchor: () => e.anchor,
getActions: () => actions,
getActionsContext: () => element,
actionRunner: new MultipleSelectionActionRunner(() => this.getSelectedResources())
actionRunner: new RepositoryPaneActionRunner(() => this.getSelectedResources(), () => this.tree.domFocus())
});
}

View File

@@ -104,14 +104,7 @@ export class ReplaceService implements IReplaceService {
const edits: WorkspaceTextEdit[] = this.createEdits(arg, resource);
await this.bulkEditorService.apply({ edits }, { progress });
return Promise.all(edits.map(e => {
const model = this.textFileService.files.get(e.resource);
if (model) {
return model.save();
}
return Promise.resolve(undefined);
}));
return Promise.all(edits.map(e => this.textFileService.files.get(e.resource)?.save()));
}
async openReplacePreview(element: FileMatchOrMatch, preserveFocus?: boolean, sideBySide?: boolean, pinned?: boolean): Promise<any> {

View File

@@ -9,7 +9,7 @@ import * as aria from 'vs/base/browser/ui/aria/aria';
import { MessageType } from 'vs/base/browser/ui/inputbox/inputBox';
import { IIdentityProvider } from 'vs/base/browser/ui/list/list';
import { ITreeContextMenuEvent, ITreeElement } from 'vs/base/browser/ui/tree/tree';
import { IAction } from 'vs/base/common/actions';
import { IAction, ActionRunner } from 'vs/base/common/actions';
import { Delayer } from 'vs/base/common/async';
import * as errors from 'vs/base/common/errors';
import { Event } from 'vs/base/common/event';
@@ -213,7 +213,7 @@ export class SearchView extends ViewPane {
this.viewletState = this.memento.getMemento(StorageScope.WORKSPACE);
this._register(this.fileService.onDidFilesChange(e => this.onFilesChanged(e)));
this._register(this.textFileService.untitled.onDidDisposeModel(e => this.onUntitledDidDispose(e)));
this._register(this.textFileService.untitled.onDidDispose(model => this.onUntitledDidDispose(model.resource)));
this._register(this.contextService.onDidChangeWorkbenchState(() => this.onDidChangeWorkbenchState()));
this._register(this.searchHistoryService.onDidClearHistory(() => this.clearHistory()));
@@ -1584,6 +1584,7 @@ export class SearchView extends ViewPane {
const openFolderLink = dom.append(textEl,
$('a.pointer.prominent', { tabindex: 0 }, nls.localize('openFolder', "Open Folder")));
const actionRunner = new ActionRunner();
this.messageDisposables.push(dom.addDisposableListener(openFolderLink, dom.EventType.CLICK, (e: MouseEvent) => {
dom.EventHelper.stop(e, false);
@@ -1591,7 +1592,7 @@ export class SearchView extends ViewPane {
this.instantiationService.createInstance(OpenFileFolderAction, OpenFileFolderAction.ID, OpenFileFolderAction.LABEL) :
this.instantiationService.createInstance(OpenFolderAction, OpenFolderAction.ID, OpenFolderAction.LABEL);
this.actionRunner!.run(action).then(() => {
actionRunner.run(action).then(() => {
action.dispose();
}, err => {
action.dispose();

View File

@@ -53,8 +53,7 @@ class LanguageSurvey extends Disposable {
// Process model-save event every 250ms to reduce load
const onModelsSavedWorker = this._register(new RunOnceWorker<ITextFileEditorModel>(models => {
models.forEach(m => {
const model = modelService.getModel(m.resource);
if (model && model.getModeId() === data.languageId && date !== storageService.get(EDITED_LANGUAGE_DATE_KEY, StorageScope.GLOBAL)) {
if (m.getMode() === data.languageId && date !== storageService.get(EDITED_LANGUAGE_DATE_KEY, StorageScope.GLOBAL)) {
const editedCount = storageService.getNumber(EDITED_LANGUAGE_COUNT_KEY, StorageScope.GLOBAL, 0) + 1;
storageService.store(EDITED_LANGUAGE_COUNT_KEY, editedCount, StorageScope.GLOBAL);
storageService.store(EDITED_LANGUAGE_DATE_KEY, date, StorageScope.GLOBAL);

View File

@@ -19,7 +19,7 @@ import ErrorTelemetry from 'vs/platform/telemetry/browser/errorTelemetry';
import { configurationTelemetry } from 'vs/platform/telemetry/common/telemetryUtils';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet';
import { ITextFileService, ITextFileModelSaveEvent, ITextFileModelLoadEvent } from 'vs/workbench/services/textfile/common/textfiles';
import { ITextFileService, ITextFileSaveEvent, ITextFileLoadEvent } from 'vs/workbench/services/textfile/common/textfiles';
import { extname, basename, isEqual, isEqualOrParent, joinPath } from 'vs/base/common/resources';
import { URI } from 'vs/base/common/uri';
import { Schemas } from 'vs/base/common/network';
@@ -58,7 +58,7 @@ export class TelemetryContribution extends Disposable implements IWorkbenchContr
@IWorkbenchEnvironmentService private readonly environmentService: IWorkbenchEnvironmentService,
@IConfigurationService configurationService: IConfigurationService,
@IViewletService viewletService: IViewletService,
@ITextFileService textFileService: ITextFileService,
@ITextFileService textFileService: ITextFileService
) {
super();
@@ -131,7 +131,7 @@ export class TelemetryContribution extends Disposable implements IWorkbenchContr
this._register(lifecycleService.onShutdown(() => this.dispose()));
}
private onTextFileModelLoaded(e: ITextFileModelLoadEvent): void {
private onTextFileModelLoaded(e: ITextFileLoadEvent): void {
const settingsType = this.getTypeIfSettings(e.model.resource);
if (settingsType) {
type SettingsReadClassification = {
@@ -146,7 +146,7 @@ export class TelemetryContribution extends Disposable implements IWorkbenchContr
}
}
private onTextFileModelSaved(e: ITextFileModelSaveEvent): void {
private onTextFileModelSaved(e: ITextFileSaveEvent): void {
const settingsType = this.getTypeIfSettings(e.model.resource);
if (settingsType) {
type SettingsWrittenClassification = {

View File

@@ -690,6 +690,8 @@ export class RunActiveFileInTerminalAction extends Action {
if (!instance) {
return Promise.resolve(undefined);
}
await instance.processReady;
const editor = this.codeEditorService.getActiveCodeEditor();
if (!editor || !editor.hasModel()) {
return Promise.resolve(undefined);

View File

@@ -21,7 +21,7 @@ import { IModelService } from 'vs/editor/common/services/modelService';
import { IModeService } from 'vs/editor/common/services/modeService';
import { ITextModelContentProvider, ITextModelService } from 'vs/editor/common/services/resolverService';
import { localize } from 'vs/nls';
import { IMenuItem, MenuId, MenuRegistry } from 'vs/platform/actions/common/actions';
import { MenuId, MenuRegistry } from 'vs/platform/actions/common/actions';
import { CommandsRegistry } from 'vs/platform/commands/common/commands';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { ContextKeyExpr, IContextKey, IContextKeyService, RawContextKey, ContextKeyRegexExpr } from 'vs/platform/contextkey/common/contextkey';
@@ -31,7 +31,7 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti
import { INotificationService, Severity } from 'vs/platform/notification/common/notification';
import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { CONTEXT_SYNC_STATE, getSyncSourceFromRemoteContentResource, getUserDataSyncStore, ISyncConfiguration, IUserDataAuthTokenService, IUserDataAutoSyncService, IUserDataSyncService, IUserDataSyncStore, registerConfiguration, SyncSource, SyncStatus, toRemoteContentResource, UserDataSyncError, UserDataSyncErrorCode, USER_DATA_SYNC_SCHEME, IUserDataSyncEnablementService, ResourceKey, getSyncSourceFromPreviewResource } from 'vs/platform/userDataSync/common/userDataSync';
import { CONTEXT_SYNC_STATE, getSyncSourceFromRemoteContentResource, getUserDataSyncStore, ISyncConfiguration, IUserDataAuthTokenService, IUserDataAutoSyncService, IUserDataSyncService, IUserDataSyncStore, registerConfiguration, SyncSource, SyncStatus, toRemoteContentResource, UserDataSyncError, UserDataSyncErrorCode, USER_DATA_SYNC_SCHEME, IUserDataSyncEnablementService, ResourceKey, getSyncSourceFromPreviewResource, CONTEXT_SYNC_ENABLEMENT } from 'vs/platform/userDataSync/common/userDataSync';
import { FloatingClickWidget } from 'vs/workbench/browser/parts/editor/editorWidgets';
import { GLOBAL_ACTIVITY_ID } from 'vs/workbench/common/activity';
import { IWorkbenchContribution } from 'vs/workbench/common/contributions';
@@ -52,7 +52,6 @@ const enum AuthStatus {
SignedOut = 'SignedOut',
Unavailable = 'Unavailable'
}
const CONTEXT_SYNC_ENABLEMENT = new RawContextKey<boolean>('syncEnabled', false);
const CONTEXT_AUTH_TOKEN_STATE = new RawContextKey<string>('authTokenStatus', AuthStatus.Initializing);
const CONTEXT_CONFLICTS_SOURCES = new RawContextKey<string>('conflictsSources', '');
@@ -547,7 +546,6 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo
} else {
await this.userDataSyncService.resetLocal();
}
await this.signOut();
this.disableSync();
}
}
@@ -574,13 +572,6 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo
}
}
private async signOut(): Promise<void> {
if (this.activeAccount) {
await this.authenticationService.logout(this.userDataSyncStore!.authenticationProviderId, this.activeAccount.id);
await this.setActiveAccount(undefined);
}
}
private getConflictsEditorInput(source: SyncSource): IEditorInput | undefined {
const previewResource = source === SyncSource.Settings ? this.workbenchEnvironmentService.settingsSyncPreviewResource
: source === SyncSource.Keybindings ? this.workbenchEnvironmentService.keybindingsSyncPreviewResource
@@ -652,6 +643,14 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo
},
when: turnOnSyncWhenContext,
});
MenuRegistry.appendMenuItem(MenuId.MenubarPreferencesMenu, {
group: '5_sync',
command: {
id: turnOnSyncCommandId,
title: localize('global activity turn on sync', "Turn on Sync...")
},
when: turnOnSyncWhenContext,
});
const signInCommandId = 'workbench.userData.actions.signin';
const signInWhenContext = ContextKeyExpr.and(CONTEXT_SYNC_STATE.notEqualsTo(SyncStatus.Uninitialized), CONTEXT_SYNC_ENABLEMENT, CONTEXT_AUTH_TOKEN_STATE.isEqualTo(AuthStatus.SignedOut));
@@ -697,6 +696,14 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo
},
when: ContextKeyExpr.and(CONTEXT_SYNC_STATE.notEqualsTo(SyncStatus.Uninitialized), CONTEXT_SYNC_ENABLEMENT),
});
MenuRegistry.appendMenuItem(MenuId.MenubarPreferencesMenu, {
group: '5_sync',
command: {
id: stopSyncCommandId,
title: localize('global activity stop sync', "Turn off Sync")
},
when: ContextKeyExpr.and(CONTEXT_SYNC_STATE.notEqualsTo(SyncStatus.Uninitialized), CONTEXT_SYNC_ENABLEMENT),
});
const resolveSettingsConflictsCommandId = 'workbench.userData.actions.resolveSettingsConflicts';
const resolveSettingsConflictsWhenContext = ContextKeyRegexExpr.create(CONTEXT_CONFLICTS_SOURCES.keys()[0], /.*settings.*/i);
@@ -736,17 +743,6 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo
when: resolveKeybindingsConflictsWhenContext,
});
const signOutMenuItem: IMenuItem = {
group: '5_sync',
command: {
id: 'workbench.userData.actions.signout',
title: localize('sign out', "Sync: Sign out")
},
when: ContextKeyExpr.and(CONTEXT_AUTH_TOKEN_STATE.isEqualTo(AuthStatus.SignedIn)),
};
CommandsRegistry.registerCommand(signOutMenuItem.command.id, () => this.signOut());
MenuRegistry.appendMenuItem(MenuId.CommandPalette, signOutMenuItem);
const configureSyncCommandId = 'workbench.userData.actions.configureSync';
CommandsRegistry.registerCommand(configureSyncCommandId, () => this.configureSyncOptions());
MenuRegistry.appendMenuItem(MenuId.CommandPalette, {
@@ -767,15 +763,6 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo
when: ContextKeyExpr.and(CONTEXT_SYNC_STATE.notEqualsTo(SyncStatus.Uninitialized)),
});
const resetLocalCommandId = 'workbench.userData.actions.resetLocal';
CommandsRegistry.registerCommand(resetLocalCommandId, () => this.userDataSyncService.resetLocal());
MenuRegistry.appendMenuItem(MenuId.CommandPalette, {
command: {
id: resetLocalCommandId,
title: localize('reset local', "Developer: Reset Local (Sync)")
},
when: ContextKeyExpr.and(CONTEXT_SYNC_STATE.notEqualsTo(SyncStatus.Uninitialized)),
});
}
}

View File

@@ -26,10 +26,12 @@ export class WebviewInput extends EditorInput {
private readonly _onDisposeWebview = this._register(new Emitter<void>());
readonly onDisposeWebview = this._onDisposeWebview.event;
readonly resource = URI.from({
scheme: WebviewPanelResourceScheme,
path: `webview-panel/webview-${this.id}`
});
get resource() {
return URI.from({
scheme: WebviewPanelResourceScheme,
path: `webview-panel/webview-${this.id}`
});
}
constructor(
public readonly id: string,

View File

@@ -53,10 +53,10 @@ export class WalkThroughInput extends EditorInput {
private maxTopScroll = 0;
private maxBottomScroll = 0;
readonly resource = this.options.resource;
get resource() { return this.options.resource; }
constructor(
private options: WalkThroughInputOptions,
private readonly options: WalkThroughInputOptions,
@ITextModelService private readonly textModelResolverService: ITextModelService
) {
super();