mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-16 10:58:30 -05:00
Merge from vscode 0a7364f00514c46c9caceece15e1f82f82e3712f
This commit is contained in:
@@ -5,20 +5,68 @@
|
||||
|
||||
import { IPanelService } from 'vs/workbench/services/panel/common/panelService';
|
||||
import { IActivityService, IActivity } from 'vs/workbench/services/activity/common/activity';
|
||||
import { IDisposable, Disposable } from 'vs/base/common/lifecycle';
|
||||
import { IDisposable, Disposable, toDisposable } from 'vs/base/common/lifecycle';
|
||||
import { IActivityBarService } from 'vs/workbench/services/activityBar/browser/activityBarService';
|
||||
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
|
||||
import { IViewDescriptorService, ViewContainerLocation } from 'vs/workbench/common/views';
|
||||
import { GLOBAL_ACTIVITY_ID, ACCOUNTS_ACTIIVTY_ID } from 'vs/workbench/common/activity';
|
||||
import { Event } from 'vs/base/common/event';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
|
||||
class ViewContainerActivityByView extends Disposable {
|
||||
|
||||
private activity: IActivity | undefined = undefined;
|
||||
private activityDisposable: IDisposable = Disposable.None;
|
||||
|
||||
constructor(
|
||||
private readonly viewId: string,
|
||||
@IViewDescriptorService private readonly viewDescriptorService: IViewDescriptorService,
|
||||
@IActivityService private readonly activityService: IActivityService,
|
||||
) {
|
||||
super();
|
||||
this._register(Event.filter(this.viewDescriptorService.onDidChangeContainer, e => e.views.some(view => view.id === viewId))(() => this.update()));
|
||||
this._register(Event.filter(this.viewDescriptorService.onDidChangeLocation, e => e.views.some(view => view.id === viewId))(() => this.update()));
|
||||
}
|
||||
|
||||
setActivity(activity: IActivity): void {
|
||||
this.activity = activity;
|
||||
this.update();
|
||||
}
|
||||
|
||||
clearActivity(): void {
|
||||
this.activity = undefined;
|
||||
this.update();
|
||||
}
|
||||
|
||||
private update(): void {
|
||||
this.activityDisposable.dispose();
|
||||
const container = this.viewDescriptorService.getViewContainerByViewId(this.viewId);
|
||||
if (container && this.activity) {
|
||||
this.activityDisposable = this.activityService.showViewContainerActivity(container.id, this.activity);
|
||||
}
|
||||
}
|
||||
|
||||
dispose() {
|
||||
this.activityDisposable.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
interface IViewActivity {
|
||||
id: number;
|
||||
readonly activity: ViewContainerActivityByView;
|
||||
}
|
||||
|
||||
export class ActivityService implements IActivityService {
|
||||
|
||||
public _serviceBrand: undefined;
|
||||
|
||||
private viewActivities = new Map<string, IViewActivity>();
|
||||
|
||||
constructor(
|
||||
@IPanelService private readonly panelService: IPanelService,
|
||||
@IActivityBarService private readonly activityBarService: IActivityBarService,
|
||||
@IViewDescriptorService private readonly viewDescriptorService: IViewDescriptorService,
|
||||
@IInstantiationService private readonly instantiationService: IInstantiationService
|
||||
) { }
|
||||
|
||||
showViewContainerActivity(viewContainerId: string, { badge, clazz, priority }: IActivity): IDisposable {
|
||||
@@ -35,6 +83,32 @@ export class ActivityService implements IActivityService {
|
||||
return Disposable.None;
|
||||
}
|
||||
|
||||
showViewActivity(viewId: string, activity: IActivity): IDisposable {
|
||||
let maybeItem = this.viewActivities.get(viewId);
|
||||
|
||||
if (maybeItem) {
|
||||
maybeItem.id++;
|
||||
} else {
|
||||
maybeItem = {
|
||||
id: 1,
|
||||
activity: this.instantiationService.createInstance(ViewContainerActivityByView, viewId)
|
||||
};
|
||||
|
||||
this.viewActivities.set(viewId, maybeItem);
|
||||
}
|
||||
|
||||
const id = maybeItem.id;
|
||||
maybeItem.activity.setActivity(activity);
|
||||
|
||||
const item = maybeItem;
|
||||
return toDisposable(() => {
|
||||
if (item.id === id) {
|
||||
item.activity.dispose();
|
||||
this.viewActivities.delete(viewId);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
showAccountsActivity({ badge, clazz, priority }: IActivity): IDisposable {
|
||||
return this.activityBarService.showActivity(ACCOUNTS_ACTIIVTY_ID, badge, clazz, priority);
|
||||
}
|
||||
|
||||
@@ -3,10 +3,8 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { IDisposable, Disposable } from 'vs/base/common/lifecycle';
|
||||
import { IDisposable } from 'vs/base/common/lifecycle';
|
||||
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { IViewDescriptorService } from 'vs/workbench/common/views';
|
||||
import { Event } from 'vs/base/common/event';
|
||||
|
||||
export interface IActivity {
|
||||
readonly badge: IBadge;
|
||||
@@ -25,6 +23,11 @@ export interface IActivityService {
|
||||
*/
|
||||
showViewContainerActivity(viewContainerId: string, badge: IActivity): IDisposable;
|
||||
|
||||
/**
|
||||
* Show activity for the given view
|
||||
*/
|
||||
showViewActivity(viewId: string, badge: IActivity): IDisposable;
|
||||
|
||||
/**
|
||||
* Show accounts activity
|
||||
*/
|
||||
@@ -36,39 +39,6 @@ export interface IActivityService {
|
||||
showGlobalActivity(activity: IActivity): IDisposable;
|
||||
}
|
||||
|
||||
export class ViewContaierActivityByView extends Disposable {
|
||||
|
||||
private activity: IActivity | undefined = undefined;
|
||||
private activityDisposable: IDisposable = Disposable.None;
|
||||
|
||||
constructor(
|
||||
private readonly viewId: string,
|
||||
@IViewDescriptorService private readonly viewDescriptorService: IViewDescriptorService,
|
||||
@IActivityService private readonly activityService: IActivityService,
|
||||
) {
|
||||
super();
|
||||
this._register(Event.filter(this.viewDescriptorService.onDidChangeContainer, e => e.views.some(view => view.id === viewId))(() => this.update()));
|
||||
this._register(Event.filter(this.viewDescriptorService.onDidChangeLocation, e => e.views.some(view => view.id === viewId))(() => this.update()));
|
||||
}
|
||||
|
||||
setActivity(activity: IActivity): void {
|
||||
this.activity = activity;
|
||||
this.update();
|
||||
}
|
||||
|
||||
private update(): void {
|
||||
this.activityDisposable.dispose();
|
||||
const container = this.viewDescriptorService.getViewContainerByViewId(this.viewId);
|
||||
if (container && this.activity) {
|
||||
this.activityDisposable = this.activityService.showViewContainerActivity(container.id, this.activity);
|
||||
}
|
||||
}
|
||||
|
||||
dispose() {
|
||||
this.activityDisposable.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
export interface IBadge {
|
||||
getDescription(): string;
|
||||
}
|
||||
|
||||
@@ -94,7 +94,11 @@ export class WorkspaceService extends Disposable implements IConfigurationServic
|
||||
});
|
||||
}));
|
||||
|
||||
this._register(Registry.as<IConfigurationRegistry>(Extensions.Configuration).onDidSchemaChange(e => this.registerConfigurationSchemas()));
|
||||
const configurationRegistry = Registry.as<IConfigurationRegistry>(Extensions.Configuration);
|
||||
if (environmentService.options?.configurationDefaults) {
|
||||
configurationRegistry.registerDefaultConfigurations([environmentService.options.configurationDefaults]);
|
||||
}
|
||||
this._register(configurationRegistry.onDidSchemaChange(e => this.registerConfigurationSchemas()));
|
||||
this._register(Registry.as<IConfigurationRegistry>(Extensions.Configuration).onDidUpdateConfiguration(configurationProperties => this.onDefaultConfigurationChanged(configurationProperties)));
|
||||
|
||||
this.workspaceEditingQueue = new Queue<void>();
|
||||
|
||||
@@ -4,14 +4,14 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { equals } from 'vs/base/common/objects';
|
||||
import { toValuesTree, IConfigurationModel, IConfigurationOverrides, IConfigurationValue, IConfigurationChange, overrideIdentifierFromKey } from 'vs/platform/configuration/common/configuration';
|
||||
import { toValuesTree, IConfigurationModel, IConfigurationOverrides, IConfigurationValue, IConfigurationChange } from 'vs/platform/configuration/common/configuration';
|
||||
import { Configuration as BaseConfiguration, ConfigurationModelParser, ConfigurationModel } from 'vs/platform/configuration/common/configurationModels';
|
||||
import { IStoredWorkspaceFolder } from 'vs/platform/workspaces/common/workspaces';
|
||||
import { Workspace } from 'vs/platform/workspace/common/workspace';
|
||||
import { ResourceMap } from 'vs/base/common/map';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { WORKSPACE_SCOPES } from 'vs/workbench/services/configuration/common/configuration';
|
||||
import { OVERRIDE_PROPERTY_PATTERN } from 'vs/platform/configuration/common/configurationRegistry';
|
||||
import { OVERRIDE_PROPERTY_PATTERN, overrideIdentifierFromKey } from 'vs/platform/configuration/common/configurationRegistry';
|
||||
|
||||
export class WorkspaceConfigurationModelParser extends ConfigurationModelParser {
|
||||
|
||||
|
||||
@@ -25,7 +25,6 @@ import { coalesce } from 'vs/base/common/arrays';
|
||||
import { trim } from 'vs/base/common/strings';
|
||||
import { IModeService } from 'vs/editor/common/services/modeService';
|
||||
import { ILabelService } from 'vs/platform/label/common/label';
|
||||
import { isWindows } from 'vs/base/common/platform';
|
||||
|
||||
export abstract class AbstractFileDialogService implements IFileDialogService {
|
||||
|
||||
@@ -259,7 +258,7 @@ export abstract class AbstractFileDialogService implements IFileDialogService {
|
||||
// Build the file filter by using our known languages
|
||||
const ext: string | undefined = defaultUri ? resources.extname(defaultUri) : undefined;
|
||||
let matchingFilter: IFilter | undefined;
|
||||
const filters: IFilter[] = coalesce(this.modeService.getRegisteredLanguageNames().map(languageName => {
|
||||
const registeredLanguageFilters: IFilter[] = coalesce(this.modeService.getRegisteredLanguageNames().map(languageName => {
|
||||
const extensions = this.modeService.getExtensions(languageName);
|
||||
if (!extensions || !extensions.length) {
|
||||
return null;
|
||||
@@ -279,24 +278,20 @@ export abstract class AbstractFileDialogService implements IFileDialogService {
|
||||
// We have no matching filter, e.g. because the language
|
||||
// is unknown. We still add the extension to the list of
|
||||
// filters though so that it can be picked
|
||||
// (https://github.com/microsoft/vscode/issues/96283) but
|
||||
// only on Windows where this is an issue. Adding this to
|
||||
// macOS would result in the following bugs:
|
||||
// https://github.com/microsoft/vscode/issues/100614 and
|
||||
// https://github.com/microsoft/vscode/issues/100241
|
||||
if (isWindows && !matchingFilter && ext) {
|
||||
// (https://github.com/microsoft/vscode/issues/96283)
|
||||
if (!matchingFilter && ext) {
|
||||
matchingFilter = { name: trim(ext, '.').toUpperCase(), extensions: [trim(ext, '.')] };
|
||||
}
|
||||
|
||||
// Order of filters is
|
||||
// - File Extension Match
|
||||
// - All Files
|
||||
// - All Files (we MUST do this to fix macOS issue https://github.com/microsoft/vscode/issues/102713)
|
||||
// - File Extension Match (if any)
|
||||
// - All Languages
|
||||
// - No Extension
|
||||
options.filters = coalesce([
|
||||
matchingFilter,
|
||||
{ name: nls.localize('allFiles', "All Files"), extensions: ['*'] },
|
||||
...filters,
|
||||
matchingFilter,
|
||||
...registeredLanguageFilters,
|
||||
{ name: nls.localize('noExt', "No Extension"), extensions: [''] }
|
||||
]);
|
||||
|
||||
|
||||
@@ -874,16 +874,21 @@ export class EditorService extends Disposable implements EditorServiceImpl {
|
||||
// Derive the label from the path if not provided explicitly
|
||||
const label = resourceEditorInput.label || basename(resourceEditorInput.resource);
|
||||
|
||||
// We keep track of the preferred resource this input is to be created
|
||||
// with but it may be different from the canonical resource (see below)
|
||||
const preferredResource = resourceEditorInput.resource;
|
||||
|
||||
// From this moment on, only operate on the canonical resource
|
||||
// to ensure we reduce the chance of opening the same resource
|
||||
// with different resource forms (e.g. path casing on Windows)
|
||||
const canonicalResource = this.asCanonicalEditorResource(resourceEditorInput.resource);
|
||||
const canonicalResource = this.asCanonicalEditorResource(preferredResource);
|
||||
|
||||
|
||||
return this.createOrGetCached(canonicalResource, () => {
|
||||
|
||||
// File
|
||||
if (resourceEditorInput.forceFile /* fix for https://github.com/Microsoft/vscode/issues/48275 */ || this.fileService.canHandleResource(canonicalResource)) {
|
||||
return this.fileEditorInputFactory.createFileEditorInput(canonicalResource, resourceEditorInput.resource, resourceEditorInput.encoding, resourceEditorInput.mode, this.instantiationService);
|
||||
if (resourceEditorInput.forceFile || this.fileService.canHandleResource(canonicalResource)) {
|
||||
return this.fileEditorInputFactory.createFileEditorInput(canonicalResource, preferredResource, resourceEditorInput.encoding, resourceEditorInput.mode, this.instantiationService);
|
||||
}
|
||||
|
||||
// Resource
|
||||
@@ -897,7 +902,7 @@ export class EditorService extends Disposable implements EditorServiceImpl {
|
||||
|
||||
// Files
|
||||
else if (!(cachedInput instanceof ResourceEditorInput)) {
|
||||
cachedInput.setLabel(resourceEditorInput.resource);
|
||||
cachedInput.setPreferredResource(preferredResource);
|
||||
|
||||
if (resourceEditorInput.encoding) {
|
||||
cachedInput.setPreferredEncoding(resourceEditorInput.encoding);
|
||||
|
||||
@@ -17,8 +17,8 @@ export interface INativeWorkbenchEnvironmentService extends IWorkbenchEnvironmen
|
||||
|
||||
readonly configuration: INativeEnvironmentConfiguration;
|
||||
|
||||
readonly disableCrashReporter: boolean;
|
||||
readonly crashReporterDirectory?: string;
|
||||
readonly crashReporterId?: string;
|
||||
|
||||
readonly cliPath: string;
|
||||
|
||||
|
||||
@@ -18,7 +18,9 @@ import { asText, isSuccess, IRequestService } from 'vs/platform/request/common/r
|
||||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
import { CancellationToken } from 'vs/base/common/cancellation';
|
||||
import { IGalleryExtension } from 'vs/platform/extensionManagement/common/extensionManagement';
|
||||
import { groupByExtension, areSameExtensions } from 'vs/platform/extensionManagement/common/extensionManagementUtil';
|
||||
import { groupByExtension, areSameExtensions, getGalleryExtensionId } from 'vs/platform/extensionManagement/common/extensionManagementUtil';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { IStaticExtension } from 'vs/workbench/workbench.web.api';
|
||||
|
||||
interface IUserExtension {
|
||||
identifier: IExtensionIdentifier;
|
||||
@@ -46,27 +48,60 @@ export class WebExtensionsScannerService implements IWebExtensionsScannerService
|
||||
|
||||
declare readonly _serviceBrand: undefined;
|
||||
|
||||
private readonly systemExtensionsPromise: Promise<IScannedExtension[]>;
|
||||
private readonly staticExtensions: IScannedExtension[];
|
||||
private readonly extensionsResource: URI | undefined;
|
||||
private readonly userExtensionsResourceLimiter: Queue<IUserExtension[]>;
|
||||
private readonly systemExtensionsPromise: Promise<IScannedExtension[]> = Promise.resolve([]);
|
||||
private readonly defaultExtensionsPromise: Promise<IScannedExtension[]> = Promise.resolve([]);
|
||||
private readonly extensionsResource: URI | undefined = undefined;
|
||||
private readonly userExtensionsResourceLimiter: Queue<IUserExtension[]> = new Queue<IUserExtension[]>();
|
||||
|
||||
constructor(
|
||||
@IWorkbenchEnvironmentService environmentService: IWorkbenchEnvironmentService,
|
||||
@IWorkbenchEnvironmentService private readonly environmentService: IWorkbenchEnvironmentService,
|
||||
@IBuiltinExtensionsScannerService private readonly builtinExtensionsScannerService: IBuiltinExtensionsScannerService,
|
||||
@IFileService private readonly fileService: IFileService,
|
||||
@IRequestService private readonly requestService: IRequestService,
|
||||
@ILogService private readonly logService: ILogService,
|
||||
@IConfigurationService private readonly configurationService: IConfigurationService,
|
||||
) {
|
||||
this.extensionsResource = isWeb ? joinPath(environmentService.userRoamingDataHome, 'extensions.json') : undefined;
|
||||
this.userExtensionsResourceLimiter = new Queue<IUserExtension[]>();
|
||||
this.systemExtensionsPromise = isWeb ? this.builtinExtensionsScannerService.scanBuiltinExtensions() : Promise.resolve([]);
|
||||
const staticExtensions = environmentService.options && Array.isArray(environmentService.options.staticExtensions) ? environmentService.options.staticExtensions : [];
|
||||
this.staticExtensions = staticExtensions.map(data => <IScannedExtension>{
|
||||
location: data.extensionLocation,
|
||||
if (isWeb) {
|
||||
this.extensionsResource = joinPath(environmentService.userRoamingDataHome, 'extensions.json');
|
||||
this.systemExtensionsPromise = this.builtinExtensionsScannerService.scanBuiltinExtensions();
|
||||
this.defaultExtensionsPromise = this.readDefaultExtensions();
|
||||
}
|
||||
}
|
||||
|
||||
private async readDefaultExtensions(): Promise<IScannedExtension[]> {
|
||||
const staticExtensions = this.environmentService.options && Array.isArray(this.environmentService.options.staticExtensions) ? this.environmentService.options.staticExtensions : [];
|
||||
const defaultUserWebExtensions = await this.readDefaultUserWebExtensions();
|
||||
return [...staticExtensions, ...defaultUserWebExtensions].map<IScannedExtension>(e => ({
|
||||
identifier: { id: getGalleryExtensionId(e.packageJSON.publisher, e.packageJSON.name) },
|
||||
location: e.extensionLocation,
|
||||
type: ExtensionType.User,
|
||||
packageJSON: data.packageJSON,
|
||||
});
|
||||
packageJSON: e.packageJSON,
|
||||
}));
|
||||
}
|
||||
|
||||
private async readDefaultUserWebExtensions(): Promise<IStaticExtension[]> {
|
||||
const result: IStaticExtension[] = [];
|
||||
const defaultUserWebExtensions = this.configurationService.getValue<{ location: string }[]>('_extensions.defaultUserWebExtensions') || [];
|
||||
for (const webExtension of defaultUserWebExtensions) {
|
||||
const extensionLocation = URI.parse(webExtension.location);
|
||||
const manifestLocation = joinPath(extensionLocation, 'package.json');
|
||||
const context = await this.requestService.request({ type: 'GET', url: manifestLocation.toString(true) }, CancellationToken.None);
|
||||
if (!isSuccess(context)) {
|
||||
this.logService.warn('Skipped default user web extension as there is an error while fetching manifest', manifestLocation);
|
||||
continue;
|
||||
}
|
||||
const content = await asText(context);
|
||||
if (!content) {
|
||||
this.logService.warn('Skipped default user web extension as there is manifest is not found', manifestLocation);
|
||||
continue;
|
||||
}
|
||||
const packageJSON = JSON.parse(content);
|
||||
result.push({
|
||||
packageJSON,
|
||||
extensionLocation,
|
||||
});
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
async scanExtensions(type?: ExtensionType): Promise<IScannedExtension[]> {
|
||||
@@ -76,7 +111,8 @@ export class WebExtensionsScannerService implements IWebExtensionsScannerService
|
||||
extensions.push(...systemExtensions);
|
||||
}
|
||||
if (type === undefined || type === ExtensionType.User) {
|
||||
extensions.push(...this.staticExtensions);
|
||||
const staticExtensions = await this.defaultExtensionsPromise;
|
||||
extensions.push(...staticExtensions);
|
||||
const userExtensions = await this.scanUserExtensions();
|
||||
extensions.push(...userExtensions);
|
||||
}
|
||||
|
||||
@@ -587,8 +587,10 @@ export class ExtensionService extends AbstractExtensionService implements IExten
|
||||
|
||||
this._doHandleExtensionPoints(this._registry.getAllExtensionDescriptions());
|
||||
|
||||
const localProcessExtensionHost = this._getExtensionHostManager(ExtensionHostKind.LocalProcess)!;
|
||||
localProcessExtensionHost.start(localProcessExtensions.map(extension => extension.identifier).filter(id => this._registry.containsExtension(id)));
|
||||
const localProcessExtensionHost = this._getExtensionHostManager(ExtensionHostKind.LocalProcess);
|
||||
if (localProcessExtensionHost) {
|
||||
localProcessExtensionHost.start(localProcessExtensions.map(extension => extension.identifier).filter(id => this._registry.containsExtension(id)));
|
||||
}
|
||||
|
||||
const localWebWorkerExtensionHost = this._getExtensionHostManager(ExtensionHostKind.LocalWebWorker);
|
||||
if (localWebWorkerExtensionHost) {
|
||||
|
||||
@@ -44,6 +44,7 @@ import { joinPath } from 'vs/base/common/resources';
|
||||
import { Registry } from 'vs/platform/registry/common/platform';
|
||||
import { IOutputChannelRegistry, Extensions } from 'vs/workbench/services/output/common/output';
|
||||
import { INativeWorkbenchEnvironmentService } from 'vs/workbench/services/environment/electron-browser/environmentService';
|
||||
import { isUUID } from 'vs/base/common/uuid';
|
||||
|
||||
export interface ILocalProcessExtensionHostInitData {
|
||||
readonly autoStart: boolean;
|
||||
@@ -182,18 +183,23 @@ export class LocalProcessExtensionHost implements IExtensionHost {
|
||||
opts.execArgv = ['--inspect-port=0'];
|
||||
}
|
||||
|
||||
// Enable the crash reporter depending on environment for local reporting
|
||||
const crashesDirectory = this._environmentService.crashReporterDirectory;
|
||||
if (crashesDirectory) {
|
||||
const crashReporterOptions: CrashReporterStartOptions = {
|
||||
// On linux crash reporter needs to be started on child node processes explicitly
|
||||
if (platform.isLinux) {
|
||||
const crashReporterStartOptions: CrashReporterStartOptions = {
|
||||
companyName: this._productService.crashReporter?.companyName || 'Microsoft',
|
||||
productName: this._productService.crashReporter?.productName || this._productService.nameShort,
|
||||
submitURL: '',
|
||||
uploadToServer: false,
|
||||
crashesDirectory
|
||||
uploadToServer: false
|
||||
};
|
||||
|
||||
opts.env.CRASH_REPORTER_START_OPTIONS = JSON.stringify(crashReporterOptions);
|
||||
const crashReporterId = this._environmentService.crashReporterId; // crashReporterId is set by the main process only when crash reporting is enabled by the user.
|
||||
const appcenter = this._productService.appCenter;
|
||||
const uploadCrashesToServer = !this._environmentService.crashReporterDirectory; // only upload unless --crash-reporter-directory is provided
|
||||
if (uploadCrashesToServer && appcenter && crashReporterId && isUUID(crashReporterId)) {
|
||||
const submitURL = appcenter[`linux-x64`];
|
||||
crashReporterStartOptions.submitURL = submitURL.concat('&uid=', crashReporterId, '&iid=', crashReporterId, '&sid=', crashReporterId);
|
||||
crashReporterStartOptions.uploadToServer = true;
|
||||
}
|
||||
opts.env.CRASH_REPORTER_START_OPTIONS = JSON.stringify(crashReporterStartOptions);
|
||||
}
|
||||
|
||||
// Run Extension Host as fork of current process
|
||||
|
||||
@@ -205,7 +205,7 @@ export class FileWalker {
|
||||
.map(arg => arg.match(/^-/) ? arg : `'${arg}'`)
|
||||
.join(' ');
|
||||
|
||||
let rgCmd = `rg ${escapedArgs}\n - cwd: ${ripgrep.cwd}`;
|
||||
let rgCmd = `${ripgrep.rgDiskPath} ${escapedArgs}\n - cwd: ${ripgrep.cwd}`;
|
||||
if (ripgrep.rgArgs.siblingClauses) {
|
||||
rgCmd += `\n - Sibling clauses: ${JSON.stringify(ripgrep.rgArgs.siblingClauses)}`;
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@ export function spawnRipgrepCmd(config: IFileQuery, folderQuery: IFolderQuery, i
|
||||
const cwd = folderQuery.folder.fsPath;
|
||||
return {
|
||||
cmd: cp.spawn(rgDiskPath, rgArgs.args, { cwd }),
|
||||
rgDiskPath,
|
||||
siblingClauses: rgArgs.siblingClauses,
|
||||
rgArgs,
|
||||
cwd
|
||||
|
||||
@@ -44,7 +44,7 @@ export class RipgrepTextSearchEngine {
|
||||
const escapedArgs = rgArgs
|
||||
.map(arg => arg.match(/^-/) ? arg : `'${arg}'`)
|
||||
.join(' ');
|
||||
this.outputChannel.appendLine(`rg ${escapedArgs}\n - cwd: ${cwd}`);
|
||||
this.outputChannel.appendLine(`${rgDiskPath} ${escapedArgs}\n - cwd: ${cwd}`);
|
||||
|
||||
let rgProc: Maybe<cp.ChildProcess> = cp.spawn(rgDiskPath, rgArgs, { cwd });
|
||||
rgProc.on('error', e => {
|
||||
@@ -57,6 +57,7 @@ export class RipgrepTextSearchEngine {
|
||||
const ripgrepParser = new RipgrepParser(options.maxResults, cwd, options.previewOptions);
|
||||
ripgrepParser.on('result', (match: TextSearchResult) => {
|
||||
gotResult = true;
|
||||
dataWithoutResult = '';
|
||||
progress.report(match);
|
||||
});
|
||||
|
||||
@@ -79,8 +80,12 @@ export class RipgrepTextSearchEngine {
|
||||
cancel();
|
||||
});
|
||||
|
||||
let dataWithoutResult = '';
|
||||
rgProc.stdout!.on('data', data => {
|
||||
ripgrepParser.handleData(data);
|
||||
if (!gotResult) {
|
||||
dataWithoutResult += data;
|
||||
}
|
||||
});
|
||||
|
||||
let gotData = false;
|
||||
@@ -96,7 +101,12 @@ export class RipgrepTextSearchEngine {
|
||||
rgProc.on('close', () => {
|
||||
this.outputChannel.appendLine(gotData ? 'Got data from stdout' : 'No data from stdout');
|
||||
this.outputChannel.appendLine(gotResult ? 'Got result from parser' : 'No result from parser');
|
||||
if (dataWithoutResult) {
|
||||
this.outputChannel.appendLine(`Got data without result: ${dataWithoutResult}`);
|
||||
}
|
||||
|
||||
this.outputChannel.appendLine('');
|
||||
|
||||
if (isDone) {
|
||||
resolve({ limitHit });
|
||||
} else {
|
||||
|
||||
@@ -446,3 +446,244 @@ export function detectEncodingFromBuffer({ buffer, bytesRead }: IReadResult, aut
|
||||
|
||||
return { seemsBinary, encoding };
|
||||
}
|
||||
|
||||
export const SUPPORTED_ENCODINGS: { [encoding: string]: { labelLong: string; labelShort: string; order: number; encodeOnly?: boolean; alias?: string } } = {
|
||||
utf8: {
|
||||
labelLong: 'UTF-8',
|
||||
labelShort: 'UTF-8',
|
||||
order: 1,
|
||||
alias: 'utf8bom'
|
||||
},
|
||||
utf8bom: {
|
||||
labelLong: 'UTF-8 with BOM',
|
||||
labelShort: 'UTF-8 with BOM',
|
||||
encodeOnly: true,
|
||||
order: 2,
|
||||
alias: 'utf8'
|
||||
},
|
||||
utf16le: {
|
||||
labelLong: 'UTF-16 LE',
|
||||
labelShort: 'UTF-16 LE',
|
||||
order: 3
|
||||
},
|
||||
utf16be: {
|
||||
labelLong: 'UTF-16 BE',
|
||||
labelShort: 'UTF-16 BE',
|
||||
order: 4
|
||||
},
|
||||
windows1252: {
|
||||
labelLong: 'Western (Windows 1252)',
|
||||
labelShort: 'Windows 1252',
|
||||
order: 5
|
||||
},
|
||||
iso88591: {
|
||||
labelLong: 'Western (ISO 8859-1)',
|
||||
labelShort: 'ISO 8859-1',
|
||||
order: 6
|
||||
},
|
||||
iso88593: {
|
||||
labelLong: 'Western (ISO 8859-3)',
|
||||
labelShort: 'ISO 8859-3',
|
||||
order: 7
|
||||
},
|
||||
iso885915: {
|
||||
labelLong: 'Western (ISO 8859-15)',
|
||||
labelShort: 'ISO 8859-15',
|
||||
order: 8
|
||||
},
|
||||
macroman: {
|
||||
labelLong: 'Western (Mac Roman)',
|
||||
labelShort: 'Mac Roman',
|
||||
order: 9
|
||||
},
|
||||
cp437: {
|
||||
labelLong: 'DOS (CP 437)',
|
||||
labelShort: 'CP437',
|
||||
order: 10
|
||||
},
|
||||
windows1256: {
|
||||
labelLong: 'Arabic (Windows 1256)',
|
||||
labelShort: 'Windows 1256',
|
||||
order: 11
|
||||
},
|
||||
iso88596: {
|
||||
labelLong: 'Arabic (ISO 8859-6)',
|
||||
labelShort: 'ISO 8859-6',
|
||||
order: 12
|
||||
},
|
||||
windows1257: {
|
||||
labelLong: 'Baltic (Windows 1257)',
|
||||
labelShort: 'Windows 1257',
|
||||
order: 13
|
||||
},
|
||||
iso88594: {
|
||||
labelLong: 'Baltic (ISO 8859-4)',
|
||||
labelShort: 'ISO 8859-4',
|
||||
order: 14
|
||||
},
|
||||
iso885914: {
|
||||
labelLong: 'Celtic (ISO 8859-14)',
|
||||
labelShort: 'ISO 8859-14',
|
||||
order: 15
|
||||
},
|
||||
windows1250: {
|
||||
labelLong: 'Central European (Windows 1250)',
|
||||
labelShort: 'Windows 1250',
|
||||
order: 16
|
||||
},
|
||||
iso88592: {
|
||||
labelLong: 'Central European (ISO 8859-2)',
|
||||
labelShort: 'ISO 8859-2',
|
||||
order: 17
|
||||
},
|
||||
cp852: {
|
||||
labelLong: 'Central European (CP 852)',
|
||||
labelShort: 'CP 852',
|
||||
order: 18
|
||||
},
|
||||
windows1251: {
|
||||
labelLong: 'Cyrillic (Windows 1251)',
|
||||
labelShort: 'Windows 1251',
|
||||
order: 19
|
||||
},
|
||||
cp866: {
|
||||
labelLong: 'Cyrillic (CP 866)',
|
||||
labelShort: 'CP 866',
|
||||
order: 20
|
||||
},
|
||||
iso88595: {
|
||||
labelLong: 'Cyrillic (ISO 8859-5)',
|
||||
labelShort: 'ISO 8859-5',
|
||||
order: 21
|
||||
},
|
||||
koi8r: {
|
||||
labelLong: 'Cyrillic (KOI8-R)',
|
||||
labelShort: 'KOI8-R',
|
||||
order: 22
|
||||
},
|
||||
koi8u: {
|
||||
labelLong: 'Cyrillic (KOI8-U)',
|
||||
labelShort: 'KOI8-U',
|
||||
order: 23
|
||||
},
|
||||
iso885913: {
|
||||
labelLong: 'Estonian (ISO 8859-13)',
|
||||
labelShort: 'ISO 8859-13',
|
||||
order: 24
|
||||
},
|
||||
windows1253: {
|
||||
labelLong: 'Greek (Windows 1253)',
|
||||
labelShort: 'Windows 1253',
|
||||
order: 25
|
||||
},
|
||||
iso88597: {
|
||||
labelLong: 'Greek (ISO 8859-7)',
|
||||
labelShort: 'ISO 8859-7',
|
||||
order: 26
|
||||
},
|
||||
windows1255: {
|
||||
labelLong: 'Hebrew (Windows 1255)',
|
||||
labelShort: 'Windows 1255',
|
||||
order: 27
|
||||
},
|
||||
iso88598: {
|
||||
labelLong: 'Hebrew (ISO 8859-8)',
|
||||
labelShort: 'ISO 8859-8',
|
||||
order: 28
|
||||
},
|
||||
iso885910: {
|
||||
labelLong: 'Nordic (ISO 8859-10)',
|
||||
labelShort: 'ISO 8859-10',
|
||||
order: 29
|
||||
},
|
||||
iso885916: {
|
||||
labelLong: 'Romanian (ISO 8859-16)',
|
||||
labelShort: 'ISO 8859-16',
|
||||
order: 30
|
||||
},
|
||||
windows1254: {
|
||||
labelLong: 'Turkish (Windows 1254)',
|
||||
labelShort: 'Windows 1254',
|
||||
order: 31
|
||||
},
|
||||
iso88599: {
|
||||
labelLong: 'Turkish (ISO 8859-9)',
|
||||
labelShort: 'ISO 8859-9',
|
||||
order: 32
|
||||
},
|
||||
windows1258: {
|
||||
labelLong: 'Vietnamese (Windows 1258)',
|
||||
labelShort: 'Windows 1258',
|
||||
order: 33
|
||||
},
|
||||
gbk: {
|
||||
labelLong: 'Simplified Chinese (GBK)',
|
||||
labelShort: 'GBK',
|
||||
order: 34
|
||||
},
|
||||
gb18030: {
|
||||
labelLong: 'Simplified Chinese (GB18030)',
|
||||
labelShort: 'GB18030',
|
||||
order: 35
|
||||
},
|
||||
cp950: {
|
||||
labelLong: 'Traditional Chinese (Big5)',
|
||||
labelShort: 'Big5',
|
||||
order: 36
|
||||
},
|
||||
big5hkscs: {
|
||||
labelLong: 'Traditional Chinese (Big5-HKSCS)',
|
||||
labelShort: 'Big5-HKSCS',
|
||||
order: 37
|
||||
},
|
||||
shiftjis: {
|
||||
labelLong: 'Japanese (Shift JIS)',
|
||||
labelShort: 'Shift JIS',
|
||||
order: 38
|
||||
},
|
||||
eucjp: {
|
||||
labelLong: 'Japanese (EUC-JP)',
|
||||
labelShort: 'EUC-JP',
|
||||
order: 39
|
||||
},
|
||||
euckr: {
|
||||
labelLong: 'Korean (EUC-KR)',
|
||||
labelShort: 'EUC-KR',
|
||||
order: 40
|
||||
},
|
||||
windows874: {
|
||||
labelLong: 'Thai (Windows 874)',
|
||||
labelShort: 'Windows 874',
|
||||
order: 41
|
||||
},
|
||||
iso885911: {
|
||||
labelLong: 'Latin/Thai (ISO 8859-11)',
|
||||
labelShort: 'ISO 8859-11',
|
||||
order: 42
|
||||
},
|
||||
koi8ru: {
|
||||
labelLong: 'Cyrillic (KOI8-RU)',
|
||||
labelShort: 'KOI8-RU',
|
||||
order: 43
|
||||
},
|
||||
koi8t: {
|
||||
labelLong: 'Tajik (KOI8-T)',
|
||||
labelShort: 'KOI8-T',
|
||||
order: 44
|
||||
},
|
||||
gb2312: {
|
||||
labelLong: 'Simplified Chinese (GB 2312)',
|
||||
labelShort: 'GB 2312',
|
||||
order: 45
|
||||
},
|
||||
cp865: {
|
||||
labelLong: 'Nordic DOS (CP 865)',
|
||||
labelShort: 'CP 865',
|
||||
order: 46
|
||||
},
|
||||
cp850: {
|
||||
labelLong: 'Western European DOS (CP 850)',
|
||||
labelShort: 'CP 850',
|
||||
order: 47
|
||||
}
|
||||
};
|
||||
|
||||
@@ -75,6 +75,9 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil
|
||||
private bufferSavedVersionId: number | undefined;
|
||||
private ignoreDirtyOnModelContentChange = false;
|
||||
|
||||
private static readonly UNDO_REDO_SAVE_PARTICIPANTS_AUTO_SAVE_THROTTLE_THRESHOLD = 500;
|
||||
private lastModelContentChangeFromUndoRedo: number | undefined = undefined;
|
||||
|
||||
private lastResolvedFileStat: IFileStatWithMetadata | undefined;
|
||||
|
||||
private readonly saveSequentializer = new TaskSequentializer();
|
||||
@@ -450,16 +453,23 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil
|
||||
// where `value` was captured in the content change listener closure scope.
|
||||
|
||||
// Content Change
|
||||
this._register(model.onDidChangeContent(() => this.onModelContentChanged(model)));
|
||||
this._register(model.onDidChangeContent(e => this.onModelContentChanged(model, e.isUndoing || e.isRedoing)));
|
||||
}
|
||||
|
||||
private onModelContentChanged(model: ITextModel): void {
|
||||
private onModelContentChanged(model: ITextModel, isUndoingOrRedoing: boolean): void {
|
||||
this.logService.trace(`[text file model] onModelContentChanged() - enter`, this.resource.toString(true));
|
||||
|
||||
// In any case increment the version id because it tracks the textual content state of the model at all times
|
||||
this.versionId++;
|
||||
this.logService.trace(`[text file model] onModelContentChanged() - new versionId ${this.versionId}`, this.resource.toString(true));
|
||||
|
||||
// Remember when the user changed the model through a undo/redo operation.
|
||||
// We need this information to throttle save participants to fix
|
||||
// https://github.com/microsoft/vscode/issues/102542
|
||||
if (isUndoingOrRedoing) {
|
||||
this.lastModelContentChangeFromUndoRedo = Date.now();
|
||||
}
|
||||
|
||||
// We mark check for a dirty-state change upon model content change, unless:
|
||||
// - explicitly instructed to ignore it (e.g. from model.load())
|
||||
// - the model is readonly (in that case we never assume the change was done by the user)
|
||||
@@ -639,7 +649,31 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil
|
||||
// Save participants can also be skipped through API.
|
||||
if (this.isResolved() && !options.skipSaveParticipants) {
|
||||
try {
|
||||
await this.textFileService.files.runSaveParticipants(this, { reason: options.reason ?? SaveReason.EXPLICIT }, saveCancellation.token);
|
||||
|
||||
// Measure the time it took from the last undo/redo operation to this save. If this
|
||||
// time is below `UNDO_REDO_SAVE_PARTICIPANTS_THROTTLE_THRESHOLD`, we make sure to
|
||||
// delay the save participant for the remaining time if the reason is auto save.
|
||||
//
|
||||
// This fixes the following issue:
|
||||
// - the user has configured auto save with delay of 100ms or shorter
|
||||
// - the user has a save participant enabled that modifies the file on each save
|
||||
// - the user types into the file and the file gets saved
|
||||
// - the user triggers undo operation
|
||||
// - this will undo the save participant change but trigger the save participant right after
|
||||
// - the user has no chance to undo over the save participant
|
||||
//
|
||||
// Reported as: https://github.com/microsoft/vscode/issues/102542
|
||||
if (options.reason === SaveReason.AUTO && typeof this.lastModelContentChangeFromUndoRedo === 'number') {
|
||||
const timeFromUndoRedoToSave = Date.now() - this.lastModelContentChangeFromUndoRedo;
|
||||
if (timeFromUndoRedoToSave < TextFileEditorModel.UNDO_REDO_SAVE_PARTICIPANTS_AUTO_SAVE_THROTTLE_THRESHOLD) {
|
||||
await timeout(TextFileEditorModel.UNDO_REDO_SAVE_PARTICIPANTS_AUTO_SAVE_THROTTLE_THRESHOLD - timeFromUndoRedoToSave);
|
||||
}
|
||||
}
|
||||
|
||||
// Run save participants unless save was cancelled meanwhile
|
||||
if (!saveCancellation.token.isCancellationRequested) {
|
||||
await this.textFileService.files.runSaveParticipants(this, { reason: options.reason ?? SaveReason.EXPLICIT }, saveCancellation.token);
|
||||
}
|
||||
} catch (error) {
|
||||
this.logService.error(`[text file model] runSaveParticipants(${versionId}) - resulted in an error: ${error.toString()}`, this.resource.toString(true));
|
||||
}
|
||||
|
||||
@@ -506,244 +506,3 @@ export function toBufferOrReadable(value: string | ITextSnapshot | undefined): V
|
||||
|
||||
return new TextSnapshotReadable(value);
|
||||
}
|
||||
|
||||
export const SUPPORTED_ENCODINGS: { [encoding: string]: { labelLong: string; labelShort: string; order: number; encodeOnly?: boolean; alias?: string } } = {
|
||||
utf8: {
|
||||
labelLong: 'UTF-8',
|
||||
labelShort: 'UTF-8',
|
||||
order: 1,
|
||||
alias: 'utf8bom'
|
||||
},
|
||||
utf8bom: {
|
||||
labelLong: 'UTF-8 with BOM',
|
||||
labelShort: 'UTF-8 with BOM',
|
||||
encodeOnly: true,
|
||||
order: 2,
|
||||
alias: 'utf8'
|
||||
},
|
||||
utf16le: {
|
||||
labelLong: 'UTF-16 LE',
|
||||
labelShort: 'UTF-16 LE',
|
||||
order: 3
|
||||
},
|
||||
utf16be: {
|
||||
labelLong: 'UTF-16 BE',
|
||||
labelShort: 'UTF-16 BE',
|
||||
order: 4
|
||||
},
|
||||
windows1252: {
|
||||
labelLong: 'Western (Windows 1252)',
|
||||
labelShort: 'Windows 1252',
|
||||
order: 5
|
||||
},
|
||||
iso88591: {
|
||||
labelLong: 'Western (ISO 8859-1)',
|
||||
labelShort: 'ISO 8859-1',
|
||||
order: 6
|
||||
},
|
||||
iso88593: {
|
||||
labelLong: 'Western (ISO 8859-3)',
|
||||
labelShort: 'ISO 8859-3',
|
||||
order: 7
|
||||
},
|
||||
iso885915: {
|
||||
labelLong: 'Western (ISO 8859-15)',
|
||||
labelShort: 'ISO 8859-15',
|
||||
order: 8
|
||||
},
|
||||
macroman: {
|
||||
labelLong: 'Western (Mac Roman)',
|
||||
labelShort: 'Mac Roman',
|
||||
order: 9
|
||||
},
|
||||
cp437: {
|
||||
labelLong: 'DOS (CP 437)',
|
||||
labelShort: 'CP437',
|
||||
order: 10
|
||||
},
|
||||
windows1256: {
|
||||
labelLong: 'Arabic (Windows 1256)',
|
||||
labelShort: 'Windows 1256',
|
||||
order: 11
|
||||
},
|
||||
iso88596: {
|
||||
labelLong: 'Arabic (ISO 8859-6)',
|
||||
labelShort: 'ISO 8859-6',
|
||||
order: 12
|
||||
},
|
||||
windows1257: {
|
||||
labelLong: 'Baltic (Windows 1257)',
|
||||
labelShort: 'Windows 1257',
|
||||
order: 13
|
||||
},
|
||||
iso88594: {
|
||||
labelLong: 'Baltic (ISO 8859-4)',
|
||||
labelShort: 'ISO 8859-4',
|
||||
order: 14
|
||||
},
|
||||
iso885914: {
|
||||
labelLong: 'Celtic (ISO 8859-14)',
|
||||
labelShort: 'ISO 8859-14',
|
||||
order: 15
|
||||
},
|
||||
windows1250: {
|
||||
labelLong: 'Central European (Windows 1250)',
|
||||
labelShort: 'Windows 1250',
|
||||
order: 16
|
||||
},
|
||||
iso88592: {
|
||||
labelLong: 'Central European (ISO 8859-2)',
|
||||
labelShort: 'ISO 8859-2',
|
||||
order: 17
|
||||
},
|
||||
cp852: {
|
||||
labelLong: 'Central European (CP 852)',
|
||||
labelShort: 'CP 852',
|
||||
order: 18
|
||||
},
|
||||
windows1251: {
|
||||
labelLong: 'Cyrillic (Windows 1251)',
|
||||
labelShort: 'Windows 1251',
|
||||
order: 19
|
||||
},
|
||||
cp866: {
|
||||
labelLong: 'Cyrillic (CP 866)',
|
||||
labelShort: 'CP 866',
|
||||
order: 20
|
||||
},
|
||||
iso88595: {
|
||||
labelLong: 'Cyrillic (ISO 8859-5)',
|
||||
labelShort: 'ISO 8859-5',
|
||||
order: 21
|
||||
},
|
||||
koi8r: {
|
||||
labelLong: 'Cyrillic (KOI8-R)',
|
||||
labelShort: 'KOI8-R',
|
||||
order: 22
|
||||
},
|
||||
koi8u: {
|
||||
labelLong: 'Cyrillic (KOI8-U)',
|
||||
labelShort: 'KOI8-U',
|
||||
order: 23
|
||||
},
|
||||
iso885913: {
|
||||
labelLong: 'Estonian (ISO 8859-13)',
|
||||
labelShort: 'ISO 8859-13',
|
||||
order: 24
|
||||
},
|
||||
windows1253: {
|
||||
labelLong: 'Greek (Windows 1253)',
|
||||
labelShort: 'Windows 1253',
|
||||
order: 25
|
||||
},
|
||||
iso88597: {
|
||||
labelLong: 'Greek (ISO 8859-7)',
|
||||
labelShort: 'ISO 8859-7',
|
||||
order: 26
|
||||
},
|
||||
windows1255: {
|
||||
labelLong: 'Hebrew (Windows 1255)',
|
||||
labelShort: 'Windows 1255',
|
||||
order: 27
|
||||
},
|
||||
iso88598: {
|
||||
labelLong: 'Hebrew (ISO 8859-8)',
|
||||
labelShort: 'ISO 8859-8',
|
||||
order: 28
|
||||
},
|
||||
iso885910: {
|
||||
labelLong: 'Nordic (ISO 8859-10)',
|
||||
labelShort: 'ISO 8859-10',
|
||||
order: 29
|
||||
},
|
||||
iso885916: {
|
||||
labelLong: 'Romanian (ISO 8859-16)',
|
||||
labelShort: 'ISO 8859-16',
|
||||
order: 30
|
||||
},
|
||||
windows1254: {
|
||||
labelLong: 'Turkish (Windows 1254)',
|
||||
labelShort: 'Windows 1254',
|
||||
order: 31
|
||||
},
|
||||
iso88599: {
|
||||
labelLong: 'Turkish (ISO 8859-9)',
|
||||
labelShort: 'ISO 8859-9',
|
||||
order: 32
|
||||
},
|
||||
windows1258: {
|
||||
labelLong: 'Vietnamese (Windows 1258)',
|
||||
labelShort: 'Windows 1258',
|
||||
order: 33
|
||||
},
|
||||
gbk: {
|
||||
labelLong: 'Simplified Chinese (GBK)',
|
||||
labelShort: 'GBK',
|
||||
order: 34
|
||||
},
|
||||
gb18030: {
|
||||
labelLong: 'Simplified Chinese (GB18030)',
|
||||
labelShort: 'GB18030',
|
||||
order: 35
|
||||
},
|
||||
cp950: {
|
||||
labelLong: 'Traditional Chinese (Big5)',
|
||||
labelShort: 'Big5',
|
||||
order: 36
|
||||
},
|
||||
big5hkscs: {
|
||||
labelLong: 'Traditional Chinese (Big5-HKSCS)',
|
||||
labelShort: 'Big5-HKSCS',
|
||||
order: 37
|
||||
},
|
||||
shiftjis: {
|
||||
labelLong: 'Japanese (Shift JIS)',
|
||||
labelShort: 'Shift JIS',
|
||||
order: 38
|
||||
},
|
||||
eucjp: {
|
||||
labelLong: 'Japanese (EUC-JP)',
|
||||
labelShort: 'EUC-JP',
|
||||
order: 39
|
||||
},
|
||||
euckr: {
|
||||
labelLong: 'Korean (EUC-KR)',
|
||||
labelShort: 'EUC-KR',
|
||||
order: 40
|
||||
},
|
||||
windows874: {
|
||||
labelLong: 'Thai (Windows 874)',
|
||||
labelShort: 'Windows 874',
|
||||
order: 41
|
||||
},
|
||||
iso885911: {
|
||||
labelLong: 'Latin/Thai (ISO 8859-11)',
|
||||
labelShort: 'ISO 8859-11',
|
||||
order: 42
|
||||
},
|
||||
koi8ru: {
|
||||
labelLong: 'Cyrillic (KOI8-RU)',
|
||||
labelShort: 'KOI8-RU',
|
||||
order: 43
|
||||
},
|
||||
koi8t: {
|
||||
labelLong: 'Tajik (KOI8-T)',
|
||||
labelShort: 'KOI8-T',
|
||||
order: 44
|
||||
},
|
||||
gb2312: {
|
||||
labelLong: 'Simplified Chinese (GB 2312)',
|
||||
labelShort: 'GB 2312',
|
||||
order: 45
|
||||
},
|
||||
cp865: {
|
||||
labelLong: 'Nordic DOS (CP 865)',
|
||||
labelShort: 'CP 865',
|
||||
order: 46
|
||||
},
|
||||
cp850: {
|
||||
labelLong: 'Western European DOS (CP 850)',
|
||||
labelShort: 'CP 850',
|
||||
order: 47
|
||||
}
|
||||
};
|
||||
|
||||
@@ -11,7 +11,6 @@ import * as streams from 'vs/base/common/stream';
|
||||
import * as iconv from 'iconv-lite-umd';
|
||||
import { getPathFromAmdModule } from 'vs/base/common/amd';
|
||||
import { newWriteableBufferStream, VSBuffer, VSBufferReadableStream, streamToBufferReadableStream } from 'vs/base/common/buffer';
|
||||
import { SUPPORTED_ENCODINGS } from 'vs/workbench/services/textfile/common/textfiles';
|
||||
import { isWindows } from 'vs/base/common/platform';
|
||||
|
||||
export async function detectEncodingByBOM(file: string): Promise<typeof encoding.UTF16be | typeof encoding.UTF16le | typeof encoding.UTF8_with_bom | null> {
|
||||
@@ -427,7 +426,7 @@ suite('Encoding', () => {
|
||||
});
|
||||
|
||||
test('encodingExists', async function () {
|
||||
for (const enc in SUPPORTED_ENCODINGS) {
|
||||
for (const enc in encoding.SUPPORTED_ENCODINGS) {
|
||||
if (enc === encoding.UTF8_with_bom) {
|
||||
continue; // skip over encodings from us
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
import { IUserDataSyncService, IAuthenticationProvider, getUserDataSyncStore, isAuthenticationProvider, IUserDataAutoSyncService, SyncResource, IResourcePreview, ISyncResourcePreview, Change, IManualSyncTask } from 'vs/platform/userDataSync/common/userDataSync';
|
||||
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
|
||||
import { IUserDataSyncWorkbenchService, IUserDataSyncAccount, AccountStatus, CONTEXT_SYNC_ENABLEMENT, CONTEXT_SYNC_STATE, CONTEXT_ACCOUNT_STATE, SHOW_SYNC_LOG_COMMAND_ID, getSyncAreaLabel, IUserDataSyncPreview, IUserDataSyncResourceGroup, CONTEXT_ENABLE_MANUAL_SYNC_VIEW, MANUAL_SYNC_VIEW_ID, CONTEXT_ENABLE_ACTIVITY_VIEWS, SYNC_VIEW_CONTAINER_ID } from 'vs/workbench/services/userDataSync/common/userDataSync';
|
||||
import { IUserDataSyncWorkbenchService, IUserDataSyncAccount, AccountStatus, CONTEXT_SYNC_ENABLEMENT, CONTEXT_SYNC_STATE, CONTEXT_ACCOUNT_STATE, SHOW_SYNC_LOG_COMMAND_ID, getSyncAreaLabel, IUserDataSyncPreview, IUserDataSyncResource, CONTEXT_ENABLE_MANUAL_SYNC_VIEW, MANUAL_SYNC_VIEW_ID, CONTEXT_ENABLE_ACTIVITY_VIEWS, SYNC_VIEW_CONTAINER_ID } from 'vs/workbench/services/userDataSync/common/userDataSync';
|
||||
import { AuthenticationSession, AuthenticationSessionsChangeEvent } from 'vs/editor/common/modes';
|
||||
import { Disposable, DisposableStore } from 'vs/base/common/lifecycle';
|
||||
import { Emitter, Event } from 'vs/base/common/event';
|
||||
@@ -270,7 +270,7 @@ export class UserDataSyncWorkbenchService extends Disposable implements IUserDat
|
||||
synchronizingResources.length ? progress.report({ message: localize('syncing resource', "Syncing {0}...", getSyncAreaLabel(synchronizingResources[0][0])) }) : undefined);
|
||||
try {
|
||||
switch (action) {
|
||||
case 'merge': return await manualSyncTask.merge();
|
||||
case 'merge': return await manualSyncTask.apply();
|
||||
case 'pull': return await manualSyncTask.pull();
|
||||
case 'push': return await manualSyncTask.push();
|
||||
case 'manual': return;
|
||||
@@ -302,28 +302,28 @@ export class UserDataSyncWorkbenchService extends Disposable implements IUserDat
|
||||
|
||||
const result = await this.dialogService.show(
|
||||
Severity.Info,
|
||||
localize('Replace or Merge', "Replace or Merge"),
|
||||
localize('preferences sync', "Preferences Sync"),
|
||||
[
|
||||
localize('sync manually', "Sync Manually"),
|
||||
localize('merge', "Merge"),
|
||||
localize('replace local', "Replace Local"),
|
||||
localize('sync manually', "Sync Manually..."),
|
||||
localize('cancel', "Cancel"),
|
||||
],
|
||||
{
|
||||
cancelId: 3,
|
||||
detail: localize('first time sync detail', "It looks like you last synced from another machine.\nWould you like to replace or merge with the synced data?"),
|
||||
detail: localize('first time sync detail', "It looks like you last synced from another machine.\nWould you like to replace or merge with your data in the cloud or sync manually?"),
|
||||
}
|
||||
);
|
||||
switch (result.choice) {
|
||||
case 0:
|
||||
this.telemetryService.publicLog2<{ action: string }, FirstTimeSyncClassification>('sync/firstTimeSync', { action: 'manual' });
|
||||
return 'manual';
|
||||
case 1:
|
||||
this.telemetryService.publicLog2<{ action: string }, FirstTimeSyncClassification>('sync/firstTimeSync', { action: 'merge' });
|
||||
return 'merge';
|
||||
case 2:
|
||||
case 1:
|
||||
this.telemetryService.publicLog2<{ action: string }, FirstTimeSyncClassification>('sync/firstTimeSync', { action: 'pull' });
|
||||
return 'pull';
|
||||
case 2:
|
||||
this.telemetryService.publicLog2<{ action: string }, FirstTimeSyncClassification>('sync/firstTimeSync', { action: 'manual' });
|
||||
return 'manual';
|
||||
}
|
||||
this.telemetryService.publicLog2<{ action: string }, FirstTimeSyncClassification>('sync/firstTimeSync', { action: 'cancelled' });
|
||||
throw canceled();
|
||||
@@ -337,14 +337,7 @@ export class UserDataSyncWorkbenchService extends Disposable implements IUserDat
|
||||
await this.waitForActiveSyncViews();
|
||||
await this.viewsService.openView(MANUAL_SYNC_VIEW_ID);
|
||||
|
||||
await Event.toPromise(Event.filter(this.userDataSyncPreview.onDidChangeChanges, e => e.length === 0));
|
||||
if (this.userDataSyncPreview.conflicts.length) {
|
||||
await Event.toPromise(Event.filter(this.userDataSyncPreview.onDidChangeConflicts, e => e.length === 0));
|
||||
}
|
||||
|
||||
/* Merge to sync globalState changes */
|
||||
await task.merge();
|
||||
|
||||
const error = await Event.toPromise(this.userDataSyncPreview.onDidCompleteManualSync);
|
||||
this.userDataSyncPreview.unsetManualSyncPreview();
|
||||
|
||||
this.manualSyncViewEnablementContext.set(false);
|
||||
@@ -354,12 +347,16 @@ export class UserDataSyncWorkbenchService extends Disposable implements IUserDat
|
||||
const viewContainer = this.viewDescriptorService.getViewContainerByViewId(MANUAL_SYNC_VIEW_ID);
|
||||
this.viewsService.closeViewContainer(viewContainer!.id);
|
||||
}
|
||||
|
||||
if (error) {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
async resetSyncedData(): Promise<void> {
|
||||
const result = await this.dialogService.confirm({
|
||||
message: localize('reset', "This will clear your synced data from the cloud and stop sync on all your devices."),
|
||||
title: localize('reset title', "Reset Synced Data"),
|
||||
message: localize('reset', "This will clear your data in the cloud and stop sync on all your devices."),
|
||||
title: localize('reset title', "Clear"),
|
||||
type: 'info',
|
||||
primaryButton: localize('reset button', "Reset"),
|
||||
});
|
||||
@@ -560,16 +557,18 @@ export class UserDataSyncWorkbenchService extends Disposable implements IUserDat
|
||||
|
||||
class UserDataSyncPreview extends Disposable implements IUserDataSyncPreview {
|
||||
|
||||
private _changes: ReadonlyArray<IUserDataSyncResourceGroup> = [];
|
||||
get changes() { return Object.freeze(this._changes); }
|
||||
private _onDidChangeChanges = this._register(new Emitter<ReadonlyArray<IUserDataSyncResourceGroup>>());
|
||||
readonly onDidChangeChanges = this._onDidChangeChanges.event;
|
||||
private _resources: ReadonlyArray<IUserDataSyncResource> = [];
|
||||
get resources() { return Object.freeze(this._resources); }
|
||||
private _onDidChangeResources = this._register(new Emitter<ReadonlyArray<IUserDataSyncResource>>());
|
||||
readonly onDidChangeResources = this._onDidChangeResources.event;
|
||||
|
||||
private _conflicts: ReadonlyArray<IUserDataSyncResourceGroup> = [];
|
||||
private _conflicts: ReadonlyArray<IUserDataSyncResource> = [];
|
||||
get conflicts() { return Object.freeze(this._conflicts); }
|
||||
private _onDidChangeConflicts = this._register(new Emitter<ReadonlyArray<IUserDataSyncResourceGroup>>());
|
||||
private _onDidChangeConflicts = this._register(new Emitter<ReadonlyArray<IUserDataSyncResource>>());
|
||||
readonly onDidChangeConflicts = this._onDidChangeConflicts.event;
|
||||
|
||||
private _onDidCompleteManualSync = this._register(new Emitter<Error | undefined>());
|
||||
readonly onDidCompleteManualSync = this._onDidCompleteManualSync.event;
|
||||
private manualSync: { preview: [SyncResource, ISyncResourcePreview][], task: IManualSyncTask, disposables: DisposableStore } | undefined;
|
||||
|
||||
constructor(
|
||||
@@ -583,7 +582,7 @@ class UserDataSyncPreview extends Disposable implements IUserDataSyncPreview {
|
||||
setManualSyncPreview(task: IManualSyncTask, preview: [SyncResource, ISyncResourcePreview][]): void {
|
||||
const disposables = new DisposableStore();
|
||||
this.manualSync = { task, preview, disposables };
|
||||
this.updateChanges();
|
||||
this.updateResources();
|
||||
}
|
||||
|
||||
unsetManualSyncPreview(): void {
|
||||
@@ -591,19 +590,19 @@ class UserDataSyncPreview extends Disposable implements IUserDataSyncPreview {
|
||||
this.manualSync.disposables.dispose();
|
||||
this.manualSync = undefined;
|
||||
}
|
||||
this.updateChanges();
|
||||
this.updateResources();
|
||||
}
|
||||
|
||||
async accept(syncResource: SyncResource, resource: URI, content: string): Promise<void> {
|
||||
async accept(syncResource: SyncResource, resource: URI, content: string | null): Promise<void> {
|
||||
if (this.manualSync) {
|
||||
const syncPreview = await this.manualSync.task.accept(resource, content);
|
||||
this.updatePreview(syncPreview);
|
||||
} else {
|
||||
await this.userDataSyncService.acceptPreviewContent(syncResource, resource, content);
|
||||
await this.userDataSyncService.accept(syncResource, resource, content, false);
|
||||
}
|
||||
}
|
||||
|
||||
async merge(resource?: URI): Promise<void> {
|
||||
async merge(resource: URI): Promise<void> {
|
||||
if (!this.manualSync) {
|
||||
throw new Error('Can merge only while syncing manually');
|
||||
}
|
||||
@@ -611,6 +610,41 @@ class UserDataSyncPreview extends Disposable implements IUserDataSyncPreview {
|
||||
this.updatePreview(syncPreview);
|
||||
}
|
||||
|
||||
async discard(resource: URI): Promise<void> {
|
||||
if (!this.manualSync) {
|
||||
throw new Error('Can discard only while syncing manually');
|
||||
}
|
||||
const syncPreview = await this.manualSync.task.discard(resource);
|
||||
this.updatePreview(syncPreview);
|
||||
}
|
||||
|
||||
async apply(): Promise<void> {
|
||||
if (!this.manualSync) {
|
||||
throw new Error('Can apply only while syncing manually');
|
||||
}
|
||||
|
||||
try {
|
||||
const syncPreview = await this.manualSync.task.apply();
|
||||
this.updatePreview(syncPreview);
|
||||
if (!this._resources.length) {
|
||||
this._onDidCompleteManualSync.fire(undefined);
|
||||
}
|
||||
} catch (error) {
|
||||
await this.manualSync.task.stop();
|
||||
this.updatePreview([]);
|
||||
this._onDidCompleteManualSync.fire(error);
|
||||
}
|
||||
}
|
||||
|
||||
async cancel(): Promise<void> {
|
||||
if (!this.manualSync) {
|
||||
throw new Error('Can cancel only while syncing manually');
|
||||
}
|
||||
await this.manualSync.task.stop();
|
||||
this.updatePreview([]);
|
||||
this._onDidCompleteManualSync.fire(canceled());
|
||||
}
|
||||
|
||||
async pull(): Promise<void> {
|
||||
if (!this.manualSync) {
|
||||
throw new Error('Can pull only while syncing manually');
|
||||
@@ -630,7 +664,7 @@ class UserDataSyncPreview extends Disposable implements IUserDataSyncPreview {
|
||||
private updatePreview(preview: [SyncResource, ISyncResourcePreview][]) {
|
||||
if (this.manualSync) {
|
||||
this.manualSync.preview = preview;
|
||||
this.updateChanges();
|
||||
this.updateResources();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -640,34 +674,28 @@ class UserDataSyncPreview extends Disposable implements IUserDataSyncPreview {
|
||||
this._conflicts = newConflicts;
|
||||
this._onDidChangeConflicts.fire(this.conflicts);
|
||||
}
|
||||
this.updateChanges();
|
||||
}
|
||||
|
||||
private updateChanges(): void {
|
||||
const newChanges = this.toUserDataSyncResourceGroups(
|
||||
private updateResources(): void {
|
||||
const newResources = this.toUserDataSyncResourceGroups(
|
||||
(this.manualSync?.preview || [])
|
||||
.filter(([syncResource]) => syncResource !== SyncResource.GlobalState) /* Filter Global State Changes */
|
||||
.map(([syncResource, syncResourcePreview]) =>
|
||||
([
|
||||
syncResource,
|
||||
/* remove merged previews and conflicts and with no changes and conflicts */
|
||||
syncResourcePreview.resourcePreviews.filter(r =>
|
||||
!r.merged
|
||||
&& (r.localChange !== Change.None || r.remoteChange !== Change.None)
|
||||
&& !this._conflicts.some(c => c.syncResource === syncResource && isEqual(c.local, r.localResource)))
|
||||
syncResourcePreview.resourcePreviews
|
||||
]))
|
||||
);
|
||||
if (!equals(newChanges, this._changes, (a, b) => isEqual(a.local, b.local))) {
|
||||
this._changes = newChanges;
|
||||
this._onDidChangeChanges.fire(this.changes);
|
||||
if (!equals(newResources, this._resources, (a, b) => isEqual(a.local, b.local) && a.mergeState === b.mergeState)) {
|
||||
this._resources = newResources;
|
||||
this._onDidChangeResources.fire(this.resources);
|
||||
}
|
||||
}
|
||||
|
||||
private toUserDataSyncResourceGroups(syncResourcePreviews: [SyncResource, IResourcePreview[]][]): IUserDataSyncResourceGroup[] {
|
||||
private toUserDataSyncResourceGroups(syncResourcePreviews: [SyncResource, IResourcePreview[]][]): IUserDataSyncResource[] {
|
||||
return flatten(
|
||||
syncResourcePreviews.map(([syncResource, resourcePreviews]) =>
|
||||
resourcePreviews.map<IUserDataSyncResourceGroup>(({ localResource, remoteResource, previewResource, localChange, remoteChange }) =>
|
||||
({ syncResource, local: localResource, remote: remoteResource, preview: previewResource, localChange, remoteChange })))
|
||||
resourcePreviews.map<IUserDataSyncResource>(({ localResource, remoteResource, previewResource, acceptedResource, localChange, remoteChange, mergeState }) =>
|
||||
({ syncResource, local: localResource, remote: remoteResource, merged: previewResource, accepted: acceptedResource, localChange, remoteChange, mergeState })))
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { IAuthenticationProvider, SyncStatus, SyncResource, Change } from 'vs/platform/userDataSync/common/userDataSync';
|
||||
import { IAuthenticationProvider, SyncStatus, SyncResource, Change, MergeState } from 'vs/platform/userDataSync/common/userDataSync';
|
||||
import { Event } from 'vs/base/common/event';
|
||||
import { RawContextKey } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { localize } from 'vs/nls';
|
||||
@@ -17,25 +17,27 @@ export interface IUserDataSyncAccount {
|
||||
}
|
||||
|
||||
export interface IUserDataSyncPreview {
|
||||
readonly onDidChangeChanges: Event<ReadonlyArray<IUserDataSyncResourceGroup>>;
|
||||
readonly changes: ReadonlyArray<IUserDataSyncResourceGroup>;
|
||||
readonly onDidChangeResources: Event<ReadonlyArray<IUserDataSyncResource>>;
|
||||
readonly resources: ReadonlyArray<IUserDataSyncResource>;
|
||||
|
||||
onDidChangeConflicts: Event<ReadonlyArray<IUserDataSyncResourceGroup>>;
|
||||
readonly conflicts: ReadonlyArray<IUserDataSyncResourceGroup>;
|
||||
|
||||
accept(syncResource: SyncResource, resource: URI, content: string): Promise<void>;
|
||||
accept(syncResource: SyncResource, resource: URI, content: string | null): Promise<void>;
|
||||
merge(resource?: URI): Promise<void>;
|
||||
discard(resource?: URI): Promise<void>;
|
||||
pull(): Promise<void>;
|
||||
push(): Promise<void>;
|
||||
apply(): Promise<void>;
|
||||
cancel(): Promise<void>;
|
||||
}
|
||||
|
||||
export interface IUserDataSyncResourceGroup {
|
||||
export interface IUserDataSyncResource {
|
||||
readonly syncResource: SyncResource;
|
||||
readonly local: URI;
|
||||
readonly remote: URI;
|
||||
readonly preview: URI;
|
||||
readonly merged: URI;
|
||||
readonly accepted: URI;
|
||||
readonly localChange: Change;
|
||||
readonly remoteChange: Change;
|
||||
readonly mergeState: MergeState;
|
||||
}
|
||||
|
||||
export const IUserDataSyncWorkbenchService = createDecorator<IUserDataSyncWorkbenchService>('IUserDataSyncWorkbenchService');
|
||||
|
||||
@@ -102,8 +102,8 @@ export class UserDataSyncService extends Disposable implements IUserDataSyncServ
|
||||
return this.channel.call('hasLocalData');
|
||||
}
|
||||
|
||||
acceptPreviewContent(syncResource: SyncResource, resource: URI, content: string): Promise<void> {
|
||||
return this.channel.call('acceptPreviewContent', [syncResource, resource, content]);
|
||||
accept(syncResource: SyncResource, resource: URI, content: string | null, apply: boolean): Promise<void> {
|
||||
return this.channel.call('accept', [syncResource, resource, content, apply]);
|
||||
}
|
||||
|
||||
resolveContent(resource: URI): Promise<string | null> {
|
||||
@@ -186,16 +186,26 @@ class ManualSyncTask implements IManualSyncTask {
|
||||
return this.deserializePreviews(previews);
|
||||
}
|
||||
|
||||
async accept(resource: URI, content: string): Promise<[SyncResource, ISyncResourcePreview][]> {
|
||||
async accept(resource: URI, content: string | null): Promise<[SyncResource, ISyncResourcePreview][]> {
|
||||
const previews = await this.channel.call<[SyncResource, ISyncResourcePreview][]>('accept', [resource, content]);
|
||||
return this.deserializePreviews(previews);
|
||||
}
|
||||
|
||||
async merge(resource?: URI): Promise<[SyncResource, ISyncResourcePreview][]> {
|
||||
async merge(resource: URI): Promise<[SyncResource, ISyncResourcePreview][]> {
|
||||
const previews = await this.channel.call<[SyncResource, ISyncResourcePreview][]>('merge', [resource]);
|
||||
return this.deserializePreviews(previews);
|
||||
}
|
||||
|
||||
async discard(resource: URI): Promise<[SyncResource, ISyncResourcePreview][]> {
|
||||
const previews = await this.channel.call<[SyncResource, ISyncResourcePreview][]>('discard', [resource]);
|
||||
return this.deserializePreviews(previews);
|
||||
}
|
||||
|
||||
async apply(): Promise<[SyncResource, ISyncResourcePreview][]> {
|
||||
const previews = await this.channel.call<[SyncResource, ISyncResourcePreview][]>('apply');
|
||||
return this.deserializePreviews(previews);
|
||||
}
|
||||
|
||||
pull(): Promise<void> {
|
||||
return this.channel.call('pull');
|
||||
}
|
||||
@@ -223,6 +233,7 @@ class ManualSyncTask implements IManualSyncTask {
|
||||
localResource: URI.revive(r.localResource),
|
||||
remoteResource: URI.revive(r.remoteResource),
|
||||
previewResource: URI.revive(r.previewResource),
|
||||
acceptedResource: URI.revive(r.acceptedResource),
|
||||
}))
|
||||
}
|
||||
]));
|
||||
|
||||
Reference in New Issue
Block a user