Merge from vscode f5044f0910e4aa7e7e06cb509781f3d56e729959 (#4759)

This commit is contained in:
Anthony Dresser
2019-03-29 10:54:38 -07:00
committed by GitHub
parent 37ce37979a
commit a064da642d
25 changed files with 295 additions and 149 deletions

View File

@@ -23,8 +23,8 @@ import { FormattingEdit } from 'vs/editor/contrib/format/formattingEdit';
import * as nls from 'vs/nls';
import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IStatusbarService } from 'vs/platform/statusbar/common/statusbar';
import { ILabelService } from 'vs/platform/label/common/label';
import { IDisposable } from 'vs/base/common/lifecycle';
import { LinkedList } from 'vs/base/common/linkedList';
export function alertFormattingEdits(edits: ISingleEditOperation[]): void {
@@ -86,30 +86,51 @@ export function getRealAndSyntheticDocumentFormattersOrdered(model: ITextModel):
return result;
}
export async function formatDocumentRangeWithFirstProvider(
export const enum FormattingMode {
Explicit = 1,
Silent = 2
}
export interface IFormattingEditProviderSelector {
<T extends (DocumentFormattingEditProvider | DocumentRangeFormattingEditProvider)>(formatter: T[], document: ITextModel, mode: FormattingMode): Promise<T | undefined>;
}
export abstract class FormattingConflicts {
private static readonly _selectors = new LinkedList<IFormattingEditProviderSelector>();
static setFormatterSelector(selector: IFormattingEditProviderSelector): IDisposable {
const remove = FormattingConflicts._selectors.unshift(selector);
return { dispose: remove };
}
static async select<T extends (DocumentFormattingEditProvider | DocumentRangeFormattingEditProvider)>(formatter: T[], document: ITextModel, mode: FormattingMode): Promise<T | undefined> {
if (formatter.length === 0) {
return undefined;
}
const { value: selector } = FormattingConflicts._selectors.iterator().next();
if (selector) {
return await selector(formatter, document, mode);
}
return formatter[0];
}
}
export async function formatDocumentRangeWithSelectedProvider(
accessor: ServicesAccessor,
editorOrModel: ITextModel | IActiveCodeEditor,
range: Range,
mode: FormattingMode,
token: CancellationToken
): Promise<boolean> {
): Promise<void> {
const instaService = accessor.get(IInstantiationService);
const statusBarService = accessor.get(IStatusbarService);
const labelService = accessor.get(ILabelService);
const model = isCodeEditor(editorOrModel) ? editorOrModel.getModel() : editorOrModel;
const [best, ...rest] = DocumentRangeFormattingEditProviderRegistry.ordered(model);
if (!best) {
return false;
const provider = DocumentRangeFormattingEditProviderRegistry.ordered(model);
const selected = await FormattingConflicts.select(provider, model, mode);
if (selected) {
await instaService.invokeFunction(formatDocumentRangeWithProvider, selected, editorOrModel, range, token);
}
const ret = await instaService.invokeFunction(formatDocumentRangeWithProvider, best, editorOrModel, range, token);
if (rest.length > 0) {
statusBarService.setStatusMessage(
nls.localize('random.pick', "$(tasklist) Formatted '{0}' with '{1}'", labelService.getUriLabel(model.uri, { relative: true }), best.displayName),
5 * 1000
);
}
return ret;
}
export async function formatDocumentRangeWithProvider(
@@ -181,29 +202,20 @@ export async function formatDocumentRangeWithProvider(
return true;
}
export async function formatDocumentWithFirstProvider(
export async function formatDocumentWithSelectedProvider(
accessor: ServicesAccessor,
editorOrModel: ITextModel | IActiveCodeEditor,
mode: FormattingMode,
token: CancellationToken
): Promise<boolean> {
): Promise<void> {
const instaService = accessor.get(IInstantiationService);
const statusBarService = accessor.get(IStatusbarService);
const labelService = accessor.get(ILabelService);
const model = isCodeEditor(editorOrModel) ? editorOrModel.getModel() : editorOrModel;
const [best, ...rest] = getRealAndSyntheticDocumentFormattersOrdered(model);
if (!best) {
return false;
const provider = getRealAndSyntheticDocumentFormattersOrdered(model);
const selected = await FormattingConflicts.select(provider, model, mode);
if (selected) {
await instaService.invokeFunction(formatDocumentWithProvider, selected, editorOrModel, token);
}
const ret = await instaService.invokeFunction(formatDocumentWithProvider, best, editorOrModel, token);
if (rest.length > 0) {
statusBarService.setStatusMessage(
nls.localize('random.pick', "$(tasklist) Formatted '{0}' with '{1}'", labelService.getUriLabel(model.uri, { relative: true }), best.displayName),
5 * 1000
);
}
return ret;
}
export async function formatDocumentWithProvider(

View File

@@ -16,7 +16,7 @@ import * as editorCommon from 'vs/editor/common/editorCommon';
import { EditorContextKeys } from 'vs/editor/common/editorContextKeys';
import { DocumentRangeFormattingEditProviderRegistry, OnTypeFormattingEditProviderRegistry } from 'vs/editor/common/modes';
import { IEditorWorkerService } from 'vs/editor/common/services/editorWorkerService';
import { getOnTypeFormattingEdits, alertFormattingEdits, formatDocumentRangeWithFirstProvider, formatDocumentWithFirstProvider } from 'vs/editor/contrib/format/format';
import { getOnTypeFormattingEdits, alertFormattingEdits, formatDocumentRangeWithSelectedProvider, formatDocumentWithSelectedProvider, FormattingMode } from 'vs/editor/contrib/format/format';
import { FormattingEdit } from 'vs/editor/contrib/format/formattingEdit';
import * as nls from 'vs/nls';
import { CommandsRegistry, ICommandService } from 'vs/platform/commands/common/commands';
@@ -211,7 +211,7 @@ class FormatOnPaste implements editorCommon.IEditorContribution {
if (this.editor.getSelections().length > 1) {
return;
}
this._instantiationService.invokeFunction(formatDocumentRangeWithFirstProvider, this.editor, range, CancellationToken.None).catch(onUnexpectedError);
this._instantiationService.invokeFunction(formatDocumentRangeWithSelectedProvider, this.editor, range, FormattingMode.Silent, CancellationToken.None).catch(onUnexpectedError);
}
}
@@ -240,7 +240,7 @@ class FormatDocumentAction extends EditorAction {
async run(accessor: ServicesAccessor, editor: ICodeEditor): Promise<void> {
if (editor.hasModel()) {
const instaService = accessor.get(IInstantiationService);
await instaService.invokeFunction(formatDocumentWithFirstProvider, editor, CancellationToken.None);
await instaService.invokeFunction(formatDocumentWithSelectedProvider, editor, FormattingMode.Explicit, CancellationToken.None);
}
}
}
@@ -276,7 +276,7 @@ class FormatSelectionAction extends EditorAction {
if (range.isEmpty()) {
range = new Range(range.startLineNumber, 1, range.startLineNumber, model.getLineMaxColumn(range.startLineNumber));
}
await instaService.invokeFunction(formatDocumentRangeWithFirstProvider, editor, range, CancellationToken.None);
await instaService.invokeFunction(formatDocumentRangeWithSelectedProvider, editor, range, FormattingMode.Explicit, CancellationToken.None);
}
}

View File

@@ -62,7 +62,7 @@ Registry.as<IConfigurationRegistry>(Extensions.Configuration)
'http.systemCertificates': {
type: 'boolean',
default: true,
description: localize('systemCertificates', "Controls whether CA certificates should be loaded from the OS. (On Windows and Mac a reload of the window is required after turning this off.)")
description: localize('systemCertificates', "Controls whether CA certificates should be loaded from the OS. (On Windows and macOS a reload of the window is required after turning this off.)")
}
}
});

View File

@@ -28,6 +28,6 @@ export class WorkspacesService implements IWorkspacesService {
}
getWorkspaceIdentifier(configPath: URI): Promise<IWorkspaceIdentifier> {
return this.channel.call('getWorkspaceIdentifier', configPath);
return this.channel.call('getWorkspaceIdentifier', configPath).then(reviveWorkspaceIdentifier);
}
}

View File

@@ -86,7 +86,7 @@ export class MainThreadExtensionService implements MainThreadExtensionServiceSha
if (missingInstalledDependency.enablementState === EnablementState.Enabled || missingInstalledDependency.enablementState === EnablementState.WorkspaceEnabled) {
this._notificationService.notify({
severity: Severity.Error,
message: localize('reload window', "Cannot activate extension '{0}' because it depends on extension '{1}', which is not loaded. Would you like to reload the window to load the extension?", extName, missingInstalledDependency.displayName),
message: localize('reload window', "Cannot activate the '{0}' extension because it depends on the '{1}' extension, which is not loaded. Would you like to reload the window to load the extension?", extName, missingInstalledDependency.displayName),
actions: {
primary: [new Action('reload', localize('reload', "Reload Window"), '', true, () => this._windowService.reloadWindow())]
}
@@ -94,7 +94,7 @@ export class MainThreadExtensionService implements MainThreadExtensionServiceSha
} else {
this._notificationService.notify({
severity: Severity.Error,
message: localize('disabledDep', "Cannot activate extension '{0}' because it depends on extension '{1}', which is disabled. Would you like to enable the extension and reload the window?", extName, missingInstalledDependency.displayName),
message: localize('disabledDep', "Cannot activate the '{0}' extension because it depends on the '{1}' extension, which is disabled. Would you like to enable the extension and reload the window?", extName, missingInstalledDependency.displayName),
actions: {
primary: [new Action('enable', localize('enable dep', "Enable and Reload"), '', true,
() => this._extensionsWorkbenchService.setEnablement([missingInstalledDependency], missingInstalledDependency.enablementState === EnablementState.Disabled ? EnablementState.Enabled : EnablementState.WorkspaceEnabled)
@@ -110,7 +110,7 @@ export class MainThreadExtensionService implements MainThreadExtensionServiceSha
if (dependencyExtension) {
this._notificationService.notify({
severity: Severity.Error,
message: localize('uninstalledDep', "Cannot activate extension '{0}' because it depends on extension '{1}', which is not installed. Would you like to install the extension and reload the window?", extName, dependencyExtension.displayName),
message: localize('uninstalledDep', "Cannot activate the '{0}' extension because it depends on the '{1}' extension, which is not installed. Would you like to install the extension and reload the window?", extName, dependencyExtension.displayName),
actions: {
primary: [new Action('install', localize('install missing dep', "Install and Reload"), '', true,
() => this._extensionsWorkbenchService.install(dependencyExtension)
@@ -118,7 +118,7 @@ export class MainThreadExtensionService implements MainThreadExtensionServiceSha
}
});
} else {
this._notificationService.error(localize('unknownDep', "Cannot activate extension '{0}' because it depends on an unknown extension '{1}'.", extName, missingDependency));
this._notificationService.error(localize('unknownDep', "Cannot activate the '{0}' extension because it depends on an unknown '{1}' extension .", extName, missingDependency));
}
}

View File

@@ -21,7 +21,7 @@ import { shouldSynchronizeModel } from 'vs/editor/common/services/modelService';
import { getCodeActions } from 'vs/editor/contrib/codeAction/codeAction';
import { applyCodeAction } from 'vs/editor/contrib/codeAction/codeActionCommands';
import { CodeActionKind } from 'vs/editor/contrib/codeAction/codeActionTrigger';
import { formatDocumentWithFirstProvider } from 'vs/editor/contrib/format/format';
import { formatDocumentWithSelectedProvider, FormattingMode } from 'vs/editor/contrib/format/format';
import { SnippetController2 } from 'vs/editor/contrib/snippet/snippetController2';
import { localize } from 'vs/nls';
import { ICommandService } from 'vs/platform/commands/common/commands';
@@ -257,7 +257,7 @@ class FormatOnSaveParticipant implements ISaveParticipantParticipant {
return new Promise<any>((resolve, reject) => {
const source = new CancellationTokenSource();
const timeout = this._configurationService.getValue<number>('editor.formatOnSaveTimeout', overrides);
const request = this._instantiationService.invokeFunction(formatDocumentWithFirstProvider, model, source.token);
const request = this._instantiationService.invokeFunction(formatDocumentWithSelectedProvider, model, FormattingMode.Silent, source.token);
setTimeout(() => {
reject(localize('timeout.formatOnSave', "Aborted format on save after {0}ms", timeout));

View File

@@ -250,7 +250,7 @@ export class MainThreadWebviews extends Disposable implements MainThreadWebviews
}
try {
await this._proxy.$deserializeWebviewPanel(handle, viewType, webview.getTitle(), state, editorGroupToViewColumn(this._editorGroupService, webview.group || ACTIVE_GROUP), webview.options);
await this._proxy.$deserializeWebviewPanel(handle, viewType, webview.getTitle(), state, editorGroupToViewColumn(this._editorGroupService, webview.group || 0), webview.options);
} catch (error) {
onUnexpectedError(error);
webview.html = MainThreadWebviews.getDeserializationFailedContents(viewType);
@@ -312,7 +312,7 @@ export class MainThreadWebviews extends Disposable implements MainThreadWebviews
this._proxy.$onDidChangeWebviewPanelViewState(newActiveWebview.handle, {
active: true,
visible: true,
position: editorGroupToViewColumn(this._editorGroupService, newActiveWebview.input.group || ACTIVE_GROUP)
position: editorGroupToViewColumn(this._editorGroupService, newActiveWebview.input.group || 0)
});
return;
}
@@ -324,7 +324,7 @@ export class MainThreadWebviews extends Disposable implements MainThreadWebviews
this._proxy.$onDidChangeWebviewPanelViewState(this._activeWebview, {
active: false,
visible: this._editorService.visibleControls.some(editor => !!editor.input && editor.input.matches(oldActiveWebview)),
position: editorGroupToViewColumn(this._editorGroupService, oldActiveWebview.group || ACTIVE_GROUP),
position: editorGroupToViewColumn(this._editorGroupService, oldActiveWebview.group || 0),
});
}
}

View File

@@ -42,7 +42,7 @@ export interface ITextEditOperation {
export interface IEditData {
documentVersionId: number;
edits: ITextEditOperation[];
setEndOfLine: EndOfLine;
setEndOfLine: EndOfLine | undefined;
undoStopBefore: boolean;
undoStopAfter: boolean;
}
@@ -52,7 +52,7 @@ export class TextEditorEdit {
private readonly _document: vscode.TextDocument;
private readonly _documentVersionId: number;
private _collectedEdits: ITextEditOperation[];
private _setEndOfLine: EndOfLine;
private _setEndOfLine: EndOfLine | undefined;
private readonly _undoStopBefore: boolean;
private readonly _undoStopAfter: boolean;
@@ -60,7 +60,7 @@ export class TextEditorEdit {
this._document = document;
this._documentVersionId = document.version;
this._collectedEdits = [];
this._setEndOfLine = 0;
this._setEndOfLine = undefined;
this._undoStopBefore = options.undoStopBefore;
this._undoStopAfter = options.undoStopAfter;
}
@@ -607,7 +607,7 @@ export class ExtHostTextEditor implements vscode.TextEditor {
});
return this._proxy.$tryApplyEdits(this._id, editData.documentVersionId, edits, {
setEndOfLine: editData.setEndOfLine && TypeConverters.EndOfLine.from(editData.setEndOfLine),
setEndOfLine: typeof editData.setEndOfLine === 'number' ? TypeConverters.EndOfLine.from(editData.setEndOfLine) : undefined,
undoStopBefore: editData.undoStopBefore,
undoStopAfter: editData.undoStopAfter
});

View File

@@ -16,7 +16,8 @@ import { hash } from 'vs/base/common/hash';
export class Call {
constructor(
readonly item: CallHierarchyItem,
readonly locations: Location[]
readonly locations: Location[],
readonly parent: Call | undefined
) { }
}
@@ -39,20 +40,20 @@ export class SingleDirectionDataSource implements IAsyncDataSource<CallHierarchy
if (!calls) {
return [];
}
return calls.map(([item, locations]) => new Call(item, locations));
return calls.map(([item, locations]) => new Call(item, locations, element));
} catch {
return [];
}
} else {
// 'root'
return [new Call(element, [{ uri: element.uri, range: Range.lift(element.range).collapseToStart() }])];
return [new Call(element, [{ uri: element.uri, range: Range.lift(element.range).collapseToStart() }], undefined)];
}
}
}
export class IdentityProvider implements IIdentityProvider<Call> {
getId(element: Call): { toString(): string; } {
return hash(element.item.uri.toString(), hash(JSON.stringify(element.item.range)));
return hash(element.item.uri.toString(), hash(JSON.stringify(element.item.range))).toString() + (element.parent ? this.getId(element.parent) : '');
}
}

View File

@@ -13,9 +13,9 @@
}
.monaco-workbench .call-hierarchy[data-state="message"] .message {
display: inherit;
text-align: center;
padding-top: 3em;
display: flex;
align-items: center;
justify-content: center;
height: 100%;
}

View File

@@ -839,6 +839,11 @@ registerEditorAction(AcceptReplInputAction);
registerEditorAction(ReplCopyAllAction);
class SelectReplActionItem extends FocusSessionActionItem {
protected getActionContext(_: string, index: number): any {
return this.debugService.getModel().getSessions(true)[index];
}
protected getSessions(): ReadonlyArray<IDebugSession> {
return this.debugService.getModel().getSessions(true).filter(s => !sessionsToIgnore.has(s));
}
@@ -856,8 +861,7 @@ class SelectReplAction extends Action {
super(id, label);
}
run(sessionName: string): Promise<any> {
const session = this.debugService.getModel().getSessions(true).filter(p => p.getLabel() === sessionName).pop();
run(session: IDebugSession): Promise<any> {
// If session is already the focused session we need to manualy update the tree since view model will not send a focused change event
if (session && session.state !== State.Inactive && session !== this.debugService.getViewModel().focusedSession) {
this.debugService.focusStackFrame(undefined, undefined, session, true);

View File

@@ -601,8 +601,8 @@ class Launch extends AbstractLaunch implements ILaunch {
revealIfVisible: true
},
}, sideBySide ? SIDE_GROUP : ACTIVE_GROUP).then(editor => ({ editor, created })));
}, (error) => {
throw new Error(nls.localize('DebugConfig.failed', "Unable to create 'launch.json' file inside the '.vscode' folder ({0}).", error));
}, (error: Error) => {
throw new Error(nls.localize('DebugConfig.failed', "Unable to create 'launch.json' file inside the '.vscode' folder ({0}).", error.message));
});
}
}

View File

@@ -11,7 +11,7 @@ import { isObject } from 'vs/base/common/types';
import { TelemetryAppenderClient } from 'vs/platform/telemetry/node/telemetryIpc';
import { IJSONSchema, IJSONSchemaSnippet } from 'vs/base/common/jsonSchema';
import { IWorkspaceFolder } from 'vs/platform/workspace/common/workspace';
import { IConfig, IDebuggerContribution, IDebugAdapterExecutable, INTERNAL_CONSOLE_OPTIONS_SCHEMA, IConfigurationManager, IDebugAdapter, ITerminalSettings, IDebugger, IDebugSession, IAdapterDescriptor, IDebugAdapterServer, IDebugConfiguration } from 'vs/workbench/contrib/debug/common/debug';
import { IConfig, IDebuggerContribution, IDebugAdapterExecutable, INTERNAL_CONSOLE_OPTIONS_SCHEMA, IConfigurationManager, IDebugAdapter, ITerminalSettings, IDebugger, IDebugSession, IAdapterDescriptor, IDebugAdapterServer } from 'vs/workbench/contrib/debug/common/debug';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { ICommandService } from 'vs/platform/commands/common/commands';
import { IOutputService } from 'vs/workbench/contrib/output/common/output';
@@ -187,15 +187,6 @@ export class Debugger implements IDebugger {
}
private inExtHost(): boolean {
const debugConfigs = this.configurationService.getValue<IDebugConfiguration>('debug');
if (typeof debugConfigs.extensionHostDebugAdapter === 'boolean') {
return debugConfigs.extensionHostDebugAdapter;
}
/*
return !!debugConfigs.extensionHostDebugAdapter
|| this.configurationManager.needsToRunInExtHost(this.type)
|| (!!this.mainExtensionDescription && this.mainExtensionDescription.extensionLocation.scheme !== 'file');
*/
return true;
}

View File

@@ -3,6 +3,5 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import 'vs/css!./media/format.contribution';
import './formatActionsMultiple';
import './formatActionsNone';

View File

@@ -6,28 +6,165 @@
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 { DocumentRangeFormattingEditProviderRegistry, DocumentFormattingEditProvider, DocumentRangeFormattingEditProvider } from 'vs/editor/common/modes';
import * as nls from 'vs/nls';
import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
import { IQuickInputService, IQuickPickItem, IQuickInputButton } from 'vs/platform/quickinput/common/quickInput';
import { IQuickInputService, IQuickPickItem } 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 { formatDocumentRangeWithProvider, formatDocumentWithProvider, getRealAndSyntheticDocumentFormattersOrdered, FormattingConflicts, FormattingMode } 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';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions';
import { Registry } from 'vs/platform/registry/common/platform';
import { IConfigurationRegistry, Extensions as ConfigurationExtensions } from 'vs/platform/configuration/common/configurationRegistry';
import { Extensions as WorkbenchExtensions, IWorkbenchContributionsRegistry, IWorkbenchContribution } from 'vs/workbench/common/contributions';
import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle';
import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
import { Disposable } from 'vs/base/common/lifecycle';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { ITextModel } from 'vs/editor/common/model';
import { INotificationService, Severity } from 'vs/platform/notification/common/notification';
import { IModeService } from 'vs/editor/common/services/modeService';
import { IStatusbarService } from 'vs/platform/statusbar/common/statusbar';
import { ILabelService } from 'vs/platform/label/common/label';
type FormattingEditProvider = DocumentFormattingEditProvider | DocumentRangeFormattingEditProvider;
class DefaultFormatter extends Disposable implements IWorkbenchContribution {
static configName = 'editor.defaultFormatter';
static extensionIds: string[] = [];
static extensionDescriptions: string[] = [];
constructor(
@IExtensionService private readonly _extensionService: IExtensionService,
@IConfigurationService private readonly _configService: IConfigurationService,
@INotificationService private readonly _notificationService: INotificationService,
@IQuickInputService private readonly _quickInputService: IQuickInputService,
@IModeService private readonly _modeService: IModeService,
@IStatusbarService private readonly _statusbarService: IStatusbarService,
@ILabelService private readonly _labelService: ILabelService,
) {
super();
this._register(this._extensionService.onDidChangeExtensions(this._updateConfigValues, this));
this._register(FormattingConflicts.setFormatterSelector((formatter, document, mode) => this._selectFormatter(formatter, document, mode)));
this._updateConfigValues();
}
private async _updateConfigValues(): Promise<void> {
const extensions = await this._extensionService.getExtensions();
DefaultFormatter.extensionIds.length = 0;
DefaultFormatter.extensionDescriptions.length = 0;
for (const extension of extensions) {
if (extension.main) {
DefaultFormatter.extensionIds.push(extension.identifier.value);
DefaultFormatter.extensionDescriptions.push(extension.description || '');
}
}
}
static _maybeQuotes(s: string): string {
return s.match(/\s/) ? `'${s}'` : s;
}
private async _selectFormatter<T extends FormattingEditProvider>(formatter: T[], document: ITextModel, mode: FormattingMode): Promise<T | undefined> {
const defaultFormatterId = this._configService.getValue<string>(DefaultFormatter.configName, {
resource: document.uri,
overrideIdentifier: document.getModeId()
});
if (defaultFormatterId) {
// good -> formatter configured
const [defaultFormatter] = formatter.filter(formatter => ExtensionIdentifier.equals(formatter.extensionId, defaultFormatterId));
if (defaultFormatter) {
// formatter available
return defaultFormatter;
} else {
// formatter gone
const extension = await this._extensionService.getExtension(defaultFormatterId);
const label = this._labelService.getUriLabel(document.uri, { relative: true });
const message = extension
? nls.localize('miss', "Extension '{0}' cannot format '{1}'", extension.displayName || extension.name, label)
: nls.localize('gone', "Extension '{0}' is configured as formatter but not available", defaultFormatterId);
this._statusbarService.setStatusMessage(message, 4000);
return undefined;
}
} else if (formatter.length === 1) {
// ok -> nothing configured but only one formatter available
return formatter[0];
}
const langName = this._modeService.getLanguageName(document.getModeId()) || document.getModeId();
const silent = mode === FormattingMode.Silent;
const message = nls.localize('config.needed', "There are multiple formatters for {0}-files. Select a default formatter to continue.", DefaultFormatter._maybeQuotes(langName));
return new Promise<T | undefined>((resolve, reject) => {
this._notificationService.prompt(
Severity.Info,
message,
[{ label: nls.localize('do.config', "Configure..."), run: () => this._pickAndPersistDefaultFormatter(formatter, document).then(resolve, reject) }],
{ silent, onCancel: resolve }
);
if (silent) {
// don't wait when formatting happens without interaction
// but pick some formatter...
resolve(formatter[0]);
}
});
}
private async _pickAndPersistDefaultFormatter<T extends FormattingEditProvider>(formatter: T[], document: ITextModel): Promise<T | undefined> {
const picks = formatter.map((formatter, index) => {
return <IIndexedPick>{
index,
label: formatter.displayName || formatter.extensionId || '?'
};
});
const langName = this._modeService.getLanguageName(document.getModeId()) || document.getModeId();
const pick = await this._quickInputService.pick(picks, { placeHolder: nls.localize('select', "Select a default formatter for {0}-files", DefaultFormatter._maybeQuotes(langName)) });
if (!pick || !formatter[pick.index].extensionId) {
return undefined;
}
this._configService.updateValue(DefaultFormatter.configName, formatter[pick.index].extensionId!.value, {
resource: document.uri,
overrideIdentifier: document.getModeId()
});
return formatter[pick.index];
}
}
Registry.as<IWorkbenchContributionsRegistry>(WorkbenchExtensions.Workbench).registerWorkbenchContribution(
DefaultFormatter,
LifecyclePhase.Restored
);
Registry.as<IConfigurationRegistry>(ConfigurationExtensions.Configuration).registerConfiguration({
id: 'editor',
order: 5,
type: 'object',
overridable: true,
properties: {
[DefaultFormatter.configName]: {
description: nls.localize('formatter.default', "Defines a default formatter which takes precedence over all other formatter settings. Must be the identifier of an extension contributing a formatter."),
type: 'string',
default: null,
enum: DefaultFormatter.extensionIds,
markdownEnumDescriptions: DefaultFormatter.extensionDescriptions
}
}
});
interface IIndexedPick extends IQuickPickItem {
index: number;
}
const openExtensionAction: IQuickInputButton = {
tooltip: nls.localize('show.ext', "Show extension..."),
iconClass: 'format-show-extension'
};
function logFormatterTelemetry<T extends { extensionId?: ExtensionIdentifier }>(telemetryService: ITelemetryService, mode: 'document' | 'range', options: T[], pick?: T) {
function extKey(obj: T): string {
@@ -48,6 +185,47 @@ function logFormatterTelemetry<T extends { extensionId?: ExtensionIdentifier }>(
});
}
async function showFormatterPick(accessor: ServicesAccessor, model: ITextModel, formatters: FormattingEditProvider[]): Promise<number | undefined> {
const quickPickService = accessor.get(IQuickInputService);
const configService = accessor.get(IConfigurationService);
const modeService = accessor.get(IModeService);
const overrides = { resource: model.uri, overrideIdentifier: model.getModeId() };
const defaultFormatter = configService.getValue<string>(DefaultFormatter.configName, overrides);
const picks = formatters.map((provider, index) => {
return <IIndexedPick>{
index,
label: provider.displayName || '',
description: ExtensionIdentifier.equals(provider.extensionId, defaultFormatter) ? nls.localize('def', "(default)") : undefined,
};
});
const configurePick: IQuickPickItem = {
label: nls.localize('config', "Configure Default Formatter...")
};
const pick = await quickPickService.pick([...picks, { type: 'separator' }, configurePick], { placeHolder: nls.localize('format.placeHolder', "Select a formatter") });
if (!pick) {
// dismissed
return undefined;
} else if (pick === configurePick) {
// config default
const langName = modeService.getLanguageName(model.getModeId()) || model.getModeId();
const pick = await quickPickService.pick(picks, { placeHolder: nls.localize('select', "Select a default formatter for {0}-files", DefaultFormatter._maybeQuotes(langName)) });
if (pick && formatters[pick.index].extensionId) {
configService.updateValue(DefaultFormatter.configName, formatters[pick.index].extensionId!.value, overrides);
}
return undefined;
} else {
// picked one
return (<IIndexedPick>pick).index;
}
}
registerEditorAction(class FormatDocumentMultipleAction extends EditorAction {
constructor() {
@@ -68,32 +246,14 @@ registerEditorAction(class FormatDocumentMultipleAction extends EditorAction {
return;
}
const instaService = accessor.get(IInstantiationService);
const quickPickService = accessor.get(IQuickInputService);
const viewletService = accessor.get(IViewletService);
const telemetryService = accessor.get(ITelemetryService);
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}`);
}
});
const pick = await instaService.invokeFunction(showFormatterPick, model, provider);
if (pick) {
await instaService.invokeFunction(formatDocumentWithProvider, provider[pick.index], editor, CancellationToken.None);
await instaService.invokeFunction(formatDocumentWithProvider, provider[pick], editor, CancellationToken.None);
}
logFormatterTelemetry(telemetryService, 'document', provider, pick && provider[pick.index]);
logFormatterTelemetry(telemetryService, 'document', provider, typeof pick === 'number' && provider[pick] || undefined);
}
});
@@ -118,36 +278,20 @@ registerEditorAction(class FormatSelectionMultipleAction extends EditorAction {
return;
}
const instaService = accessor.get(IInstantiationService);
const quickPickService = accessor.get(IQuickInputService);
const viewletService = accessor.get(IViewletService);
const telemetryService = accessor.get(ITelemetryService);
const model = editor.getModel();
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}`);
}
});
const pick = await instaService.invokeFunction(showFormatterPick, model, provider);
if (pick) {
await instaService.invokeFunction(formatDocumentRangeWithProvider, provider[pick.index], editor, range, CancellationToken.None);
await instaService.invokeFunction(formatDocumentRangeWithProvider, provider[pick], editor, range, CancellationToken.None);
}
logFormatterTelemetry(telemetryService, 'range', provider, pick && provider[pick.index]);
logFormatterTelemetry(telemetryService, 'range', provider, typeof pick === 'number' && provider[pick] || undefined);
}
});

View File

@@ -1 +0,0 @@
<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>

Before

Width:  |  Height:  |  Size: 927 B

View File

@@ -1 +0,0 @@
<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>

Before

Width:  |  Height:  |  Size: 927 B

View File

@@ -1,13 +0,0 @@
/*---------------------------------------------------------------------------------------------
* 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

@@ -1181,6 +1181,10 @@ export class SCMViewlet extends ViewContainerViewlet implements IViewModel {
this.viewsModel.setVisible(MainPanel.ID, false);
}
if (repositoryCount === 1) {
this.viewsModel.setVisible(this.viewDescriptors[0].id, true);
}
toggleClass(this.el, 'empty', repositoryCount === 0);
this.repositoryCount = repositoryCount;
}

View File

@@ -230,7 +230,7 @@ export class QueryBuilder {
parseSearchPaths(pattern: string): ISearchPathsInfo {
const isSearchPath = (segment: string) => {
// A segment is a search path if it is an absolute path or starts with ./, ../, .\, or ..\
return path.isAbsolute(segment) || /^\.\.?[\/\\]/.test(segment);
return path.isAbsolute(segment) || /^\.\.?([\/\\]|$)/.test(segment);
};
const segments = splitGlobPattern(pattern)
@@ -339,7 +339,7 @@ export class QueryBuilder {
const workspaceUri = this.workspaceContextService.getWorkspace().folders[0].uri;
searchPath = normalizeSlashes(searchPath);
if (strings.startsWith(searchPath, '../')) {
if (strings.startsWith(searchPath, '../') || searchPath === '..') {
const resolvedPath = path.posix.resolve(workspaceUri.path, searchPath);
return [{
searchPath: workspaceUri.with({ path: resolvedPath })

View File

@@ -667,6 +667,14 @@ suite('QueryBuilder', () => {
}]
}
],
[
'..',
{
searchPaths: [{
searchPath: getUri('/foo')
}]
}
],
[
'..\\bar',
{

View File

@@ -458,7 +458,7 @@ export class TerminalTaskSystem implements ITaskSystem {
const resolvedVariables = this.resolveVariablesFromSet(this.currentTask.systemInfo, this.currentTask.workspaceFolder!, task, variables);
return resolvedVariables.then((resolvedVariables) => {
if (resolvedVariables) {
if (resolvedVariables && task.command && task.command.runtime) {
this.currentTask.resolvedVariables = resolvedVariables;
return this.executeInTerminal(task, trigger, new VariableResolver(this.currentTask.workspaceFolder!, this.currentTask.systemInfo, resolvedVariables.variables, this.configurationResolverService));
} else {

View File

@@ -325,15 +325,15 @@ export class BulkEdit {
} else if (!edit.newUri && edit.oldUri) {
// delete file
if (!options.ignoreIfNotExists || await this._fileService.existsFile(edit.oldUri)) {
if (await this._fileService.existsFile(edit.oldUri)) {
let useTrash = this._configurationService.getValue<boolean>('files.enableTrash');
if (useTrash && !(await this._fileService.hasCapability(edit.oldUri, FileSystemProviderCapabilities.Trash))) {
useTrash = false; // not supported by provider
}
await this._textFileService.delete(edit.oldUri, { useTrash, recursive: options.recursive });
} else if (!options.ignoreIfNotExists) {
throw new Error(`${edit.oldUri} does not exist and can not be deleted`);
}
} else if (edit.newUri && !edit.oldUri) {
// create file
if (options.overwrite === undefined && options.ignoreIfExists && await this._fileService.existsFile(edit.newUri)) {

View File

@@ -20,7 +20,6 @@ export function setup() {
await app.workbench.extensions.installExtension(extensionName);
await app.reload();
await app.workbench.extensions.waitForExtensionsViewlet();
await app.workbench.quickopen.runCommand('Smoke Test Check');
await app.workbench.statusbar.waitForStatusbarText('smoke test', 'VS Code Smoke Test Check');

View File

@@ -113,7 +113,6 @@ export async function spawn(options: SpawnOptions): Promise<Code> {
'--skip-release-notes',
'--sticky-quickopen',
'--disable-telemetry',
'--disable-extensions',
'--disable-updates',
'--disable-crash-reporter',
`--extensions-dir=${options.extensionsPath}`,