Merge branch 'master' into aeschli/productIconsTheme

This commit is contained in:
Martin Aeschlimann 2020-03-06 16:33:17 +01:00
commit f837b41d7c
36 changed files with 434 additions and 167 deletions

74
.vscode/searches/es6.code-search vendored Normal file
View file

@ -0,0 +1,74 @@
# Query: @deprecated ES6
# Flags: CaseSensitive WordMatch
# ContextLines: 2
11 results - 3 files
src/vs/base/common/arrays.ts:
401
402 /**
403: * @deprecated ES6: use `Array.findIndex`
404 */
405 export function firstIndex<T>(array: ReadonlyArray<T>, fn: (item: T) => boolean): number {
417
418 /**
419: * @deprecated ES6: use `Array.find`
420 */
421 export function first<T>(array: ReadonlyArray<T>, fn: (item: T) => boolean, notFoundValue: T): T;
474
475 /**
476: * @deprecated ES6: use `Array.fill`
477 */
478 export function fill<T>(num: number, value: T, arr: T[] = []): T[] {
571
572 /**
573: * @deprecated ES6: use `Array.find`
574 */
575 export function find<T>(arr: ArrayLike<T>, predicate: (value: T, index: number, arr: ArrayLike<T>) => any): T | undefined {
src/vs/base/common/map.ts:
9
10 /**
11: * @deprecated ES6: use `[...SetOrMap.values()]`
12 */
13 export function values<V = any>(set: Set<V>): V[];
20
21 /**
22: * @deprecated ES6: use `[...map.keys()]`
23 */
24 export function keys<K, V>(map: Map<K, V>): K[] {
58
59 /**
60: * @deprecated ES6: use `...Map.entries()`
61 */
62 export function mapToSerializable(map: Map<string, string>): [string, string][] {
71
72 /**
73: * @deprecated ES6: use `new Map([[key1, value1],[key2, value2]])`
74 */
75 export function serializableToMap(serializable: [string, string][]): Map<string, string> {
src/vs/base/common/strings.ts:
16
17 /**
18: * @deprecated ES6: use `String.padStart`
19 */
20 export function pad(n: number, l: number, char: string = '0'): string {
147
148 /**
149: * @deprecated ES6: use `String.startsWith`
150 */
151 export function startsWith(haystack: string, needle: string): boolean {
168
169 /**
170: * @deprecated ES6: use `String.endsWith`
171 */
172 export function endsWith(haystack: string, needle: string): boolean {

View file

@ -135,6 +135,23 @@ steps:
displayName: Run integration tests (Browser)
condition: and(succeeded(), eq(variables['VSCODE_STEP_ON_IT'], 'false'))
- script: |
set -e
APP_ROOT=$(agent.builddirectory)/VSCode-darwin
APP_NAME="`ls $APP_ROOT | head -n 1`"
yarn smoketest --build "$APP_ROOT/$APP_NAME"
continueOnError: true
displayName: Run smoke tests (Electron)
condition: and(succeeded(), eq(variables['VSCODE_STEP_ON_IT'], 'false'))
- script: |
set -e
VSCODE_REMOTE_SERVER_PATH="$(agent.builddirectory)/vscode-reh-web-darwin" \
yarn smoketest --web --headless
continueOnError: true
displayName: Run smoke tests (Browser)
condition: and(succeeded(), eq(variables['VSCODE_STEP_ON_IT'], 'false'))
- script: |
set -e
security create-keychain -p pwd $(agent.tempdirectory)/buildagent.keychain

View file

@ -20,9 +20,7 @@ function main() {
}
}
if (Object.keys(content).length > 0) {
fs.writeFileSync(path.join(__dirname, '../src/common/config.json'), JSON.stringify(content));
}
fs.writeFileSync(path.join(__dirname, '../src/common/config.json'), JSON.stringify(content));
}
main();

View file

@ -180,16 +180,16 @@ class Trait<T> implements ISpliceable<boolean>, IDisposable {
}
}
class FocusTrait<T> extends Trait<T> {
class SelectionTrait<T> extends Trait<T> {
constructor(private isAriaSelected: (index: number) => boolean) {
super('focused');
constructor() {
super('selected');
}
renderIndex(index: number, container: HTMLElement): void {
super.renderIndex(index, container);
if (this.contains(index) || this.isAriaSelected(index)) {
if (this.contains(index)) {
container.setAttribute('aria-selected', 'true');
} else {
container.setAttribute('aria-selected', 'false');
@ -1198,8 +1198,8 @@ export class List<T> implements ISpliceable<T>, IDisposable {
renderers: IListRenderer<any /* TODO@joao */, any>[],
private _options: IListOptions<T> = DefaultOptions
) {
this.selection = new Trait('selected');
this.focus = new FocusTrait(this.selection.contains);
this.selection = new SelectionTrait();
this.focus = new Trait('focused');
mixin(_options, defaultStyles, false);

View file

@ -49,7 +49,7 @@ import { IFileService } from 'vs/platform/files/common/files';
import { DiskFileSystemProvider } from 'vs/platform/files/electron-browser/diskFileSystemProvider';
import { Schemas } from 'vs/base/common/network';
import { IProductService } from 'vs/platform/product/common/productService';
import { IUserDataSyncService, IUserDataSyncStoreService, registerConfiguration, IUserDataSyncLogService, IUserDataSyncUtilService, ISettingsSyncService, IUserDataSyncEnablementService } from 'vs/platform/userDataSync/common/userDataSync';
import { IUserDataSyncService, IUserDataSyncStoreService, registerConfiguration, IUserDataSyncLogService, IUserDataSyncUtilService, ISettingsSyncService, IUserDataSyncEnablementService, IUserDataSyncBackupStoreService } from 'vs/platform/userDataSync/common/userDataSync';
import { UserDataSyncService } from 'vs/platform/userDataSync/common/userDataSyncService';
import { UserDataSyncStoreService } from 'vs/platform/userDataSync/common/userDataSyncStoreService';
import { UserDataSyncChannel, UserDataSyncUtilServiceClient, SettingsSyncChannel, UserDataAutoSyncChannel, UserDataSyncStoreServiceChannel } from 'vs/platform/userDataSync/common/userDataSyncIpc';
@ -67,6 +67,7 @@ import { GlobalExtensionEnablementService } from 'vs/platform/extensionManagemen
import { UserDataSyncEnablementService } from 'vs/platform/userDataSync/common/userDataSyncEnablementService';
import { IAuthenticationTokenService, AuthenticationTokenService } from 'vs/platform/authentication/common/authentication';
import { AuthenticationTokenServiceChannel } from 'vs/platform/authentication/common/authenticationIpc';
import { UserDataSyncBackupStoreService } from 'vs/platform/userDataSync/common/userDataSyncBackupStoreService';
export interface ISharedProcessConfiguration {
readonly machineId: string;
@ -194,6 +195,7 @@ async function main(server: Server, initData: ISharedProcessInitData, configurat
services.set(IUserDataSyncUtilService, new UserDataSyncUtilServiceClient(server.getChannel('userDataSyncUtil', client => client.ctx !== 'main')));
services.set(IGlobalExtensionEnablementService, new SyncDescriptor(GlobalExtensionEnablementService));
services.set(IUserDataSyncStoreService, new SyncDescriptor(UserDataSyncStoreService));
services.set(IUserDataSyncBackupStoreService, new SyncDescriptor(UserDataSyncBackupStoreService));
services.set(IUserDataSyncEnablementService, new SyncDescriptor(UserDataSyncEnablementService));
services.set(ISettingsSyncService, new SyncDescriptor(SettingsSynchroniser));
services.set(IUserDataSyncService, new SyncDescriptor(UserDataSyncService));

View file

@ -925,6 +925,23 @@ export class MouseTargetFactory {
}
}
// For inline decorations, Gecko returns the `<span>` of the line and the offset is the `<span>` with the inline decoration
if (hitResult.offsetNode.nodeType === hitResult.offsetNode.ELEMENT_NODE) {
const parent1 = hitResult.offsetNode.parentNode; // expected to be the view line div
const parent1ClassName = parent1 && parent1.nodeType === parent1.ELEMENT_NODE ? (<HTMLElement>parent1).className : null;
if (parent1ClassName === ViewLine.CLASS_NAME) {
const tokenSpan = hitResult.offsetNode.childNodes[Math.min(hitResult.offset, hitResult.offsetNode.childNodes.length - 1)];
if (tokenSpan) {
const p = ctx.getPositionFromDOMInfo(<HTMLElement>tokenSpan, 0);
return {
position: p,
hitTarget: null
};
}
}
}
return {
position: null,
hitTarget: <HTMLElement>hitResult.offsetNode

View file

@ -779,7 +779,7 @@ export class Cursor extends viewEvents.ViewEventEmitter implements ICursors {
}
private _type(source: string, text: string): void {
if (!this._isDoingComposition && source === 'keyboard') {
if (source === 'keyboard') {
// If this event is coming straight from the keyboard, look for electric characters and enter
const len = text.length;
@ -790,7 +790,7 @@ export class Cursor extends viewEvents.ViewEventEmitter implements ICursors {
// Here we must interpret each typed character individually
const autoClosedCharacters = AutoClosedAction.getAllAutoClosedCharacters(this._autoClosedActions);
this._executeEditOperation(TypeOperations.typeWithInterceptors(this._prevEditOperationType, this.context.config, this.context.model, this.getSelections(), autoClosedCharacters, chr));
this._executeEditOperation(TypeOperations.typeWithInterceptors(this._isDoingComposition, this._prevEditOperationType, this.context.config, this.context.model, this.getSelections(), autoClosedCharacters, chr));
offset += charLength;
}

View file

@ -269,9 +269,15 @@ export class TypeOperations {
commands[i] = null;
continue;
}
let pos = selection.getPosition();
let startColumn = Math.max(1, pos.column - replaceCharCnt);
let range = new Range(pos.lineNumber, startColumn, pos.lineNumber, pos.column);
const pos = selection.getPosition();
const startColumn = Math.max(1, pos.column - replaceCharCnt);
const range = new Range(pos.lineNumber, startColumn, pos.lineNumber, pos.column);
const oldText = model.getValueInRange(range);
if (oldText === txt) {
// => ignore composition that doesn't do anything
commands[i] = null;
continue;
}
commands[i] = new ReplaceCommand(range, txt);
}
return new EditOperationResult(EditOperationType.Typing, commands, {
@ -796,9 +802,9 @@ export class TypeOperations {
return null;
}
public static typeWithInterceptors(prevEditOperationType: EditOperationType, config: CursorConfiguration, model: ITextModel, selections: Selection[], autoClosedCharacters: Range[], ch: string): EditOperationResult {
public static typeWithInterceptors(isDoingComposition: boolean, prevEditOperationType: EditOperationType, config: CursorConfiguration, model: ITextModel, selections: Selection[], autoClosedCharacters: Range[], ch: string): EditOperationResult {
if (ch === '\n') {
if (!isDoingComposition && ch === '\n') {
let commands: ICommand[] = [];
for (let i = 0, len = selections.length; i < len; i++) {
commands[i] = TypeOperations._enter(config, model, false, selections[i]);
@ -809,7 +815,7 @@ export class TypeOperations {
});
}
if (this._isAutoIndentType(config, model, selections)) {
if (!isDoingComposition && this._isAutoIndentType(config, model, selections)) {
let commands: Array<ICommand | null> = [];
let autoIndentFails = false;
for (let i = 0, len = selections.length; i < len; i++) {
@ -827,13 +833,15 @@ export class TypeOperations {
}
}
if (this._isAutoClosingOvertype(config, model, selections, autoClosedCharacters, ch)) {
if (!isDoingComposition && this._isAutoClosingOvertype(config, model, selections, autoClosedCharacters, ch)) {
return this._runAutoClosingOvertype(prevEditOperationType, config, model, selections, ch);
}
const autoClosingPairOpenCharType = this._isAutoClosingOpenCharType(config, model, selections, ch, true);
if (autoClosingPairOpenCharType) {
return this._runAutoClosingOpenCharType(prevEditOperationType, config, model, selections, ch, true, autoClosingPairOpenCharType);
if (!isDoingComposition) {
const autoClosingPairOpenCharType = this._isAutoClosingOpenCharType(config, model, selections, ch, true);
if (autoClosingPairOpenCharType) {
return this._runAutoClosingOpenCharType(prevEditOperationType, config, model, selections, ch, true, autoClosingPairOpenCharType);
}
}
if (this._isSurroundSelectionType(config, model, selections, ch)) {
@ -842,7 +850,7 @@ export class TypeOperations {
// Electric characters make sense only when dealing with a single cursor,
// as multiple cursors typing brackets for example would interfer with bracket matching
if (this._isTypeInterceptorElectricChar(config, model, selections)) {
if (!isDoingComposition && this._isTypeInterceptorElectricChar(config, model, selections)) {
const r = this._typeInterceptorElectricChar(prevEditOperationType, config, model, selections[0], ch);
if (r) {
return r;

View file

@ -108,8 +108,9 @@ export class ClickLinkGesture extends Disposable {
private readonly _editor: ICodeEditor;
private _opts: ClickLinkOptions;
private lastMouseMoveEvent: ClickLinkMouseEvent | null;
private hasTriggerKeyOnMouseDown: boolean;
private _lastMouseMoveEvent: ClickLinkMouseEvent | null;
private _hasTriggerKeyOnMouseDown: boolean;
private _lineNumberOnMouseDown: number;
constructor(editor: ICodeEditor) {
super();
@ -117,8 +118,9 @@ export class ClickLinkGesture extends Disposable {
this._editor = editor;
this._opts = createOptions(this._editor.getOption(EditorOption.multiCursorModifier));
this.lastMouseMoveEvent = null;
this.hasTriggerKeyOnMouseDown = false;
this._lastMouseMoveEvent = null;
this._hasTriggerKeyOnMouseDown = false;
this._lineNumberOnMouseDown = 0;
this._register(this._editor.onDidChangeConfiguration((e) => {
if (e.hasChanged(EditorOption.multiCursorModifier)) {
@ -127,77 +129,80 @@ export class ClickLinkGesture extends Disposable {
return;
}
this._opts = newOpts;
this.lastMouseMoveEvent = null;
this.hasTriggerKeyOnMouseDown = false;
this._lastMouseMoveEvent = null;
this._hasTriggerKeyOnMouseDown = false;
this._lineNumberOnMouseDown = 0;
this._onCancel.fire();
}
}));
this._register(this._editor.onMouseMove((e: IEditorMouseEvent) => this.onEditorMouseMove(new ClickLinkMouseEvent(e, this._opts))));
this._register(this._editor.onMouseDown((e: IEditorMouseEvent) => this.onEditorMouseDown(new ClickLinkMouseEvent(e, this._opts))));
this._register(this._editor.onMouseUp((e: IEditorMouseEvent) => this.onEditorMouseUp(new ClickLinkMouseEvent(e, this._opts))));
this._register(this._editor.onKeyDown((e: IKeyboardEvent) => this.onEditorKeyDown(new ClickLinkKeyboardEvent(e, this._opts))));
this._register(this._editor.onKeyUp((e: IKeyboardEvent) => this.onEditorKeyUp(new ClickLinkKeyboardEvent(e, this._opts))));
this._register(this._editor.onMouseDrag(() => this.resetHandler()));
this._register(this._editor.onMouseMove((e: IEditorMouseEvent) => this._onEditorMouseMove(new ClickLinkMouseEvent(e, this._opts))));
this._register(this._editor.onMouseDown((e: IEditorMouseEvent) => this._onEditorMouseDown(new ClickLinkMouseEvent(e, this._opts))));
this._register(this._editor.onMouseUp((e: IEditorMouseEvent) => this._onEditorMouseUp(new ClickLinkMouseEvent(e, this._opts))));
this._register(this._editor.onKeyDown((e: IKeyboardEvent) => this._onEditorKeyDown(new ClickLinkKeyboardEvent(e, this._opts))));
this._register(this._editor.onKeyUp((e: IKeyboardEvent) => this._onEditorKeyUp(new ClickLinkKeyboardEvent(e, this._opts))));
this._register(this._editor.onMouseDrag(() => this._resetHandler()));
this._register(this._editor.onDidChangeCursorSelection((e) => this.onDidChangeCursorSelection(e)));
this._register(this._editor.onDidChangeModel((e) => this.resetHandler()));
this._register(this._editor.onDidChangeModelContent(() => this.resetHandler()));
this._register(this._editor.onDidChangeCursorSelection((e) => this._onDidChangeCursorSelection(e)));
this._register(this._editor.onDidChangeModel((e) => this._resetHandler()));
this._register(this._editor.onDidChangeModelContent(() => this._resetHandler()));
this._register(this._editor.onDidScrollChange((e) => {
if (e.scrollTopChanged || e.scrollLeftChanged) {
this.resetHandler();
this._resetHandler();
}
}));
}
private onDidChangeCursorSelection(e: ICursorSelectionChangedEvent): void {
private _onDidChangeCursorSelection(e: ICursorSelectionChangedEvent): void {
if (e.selection && e.selection.startColumn !== e.selection.endColumn) {
this.resetHandler(); // immediately stop this feature if the user starts to select (https://github.com/Microsoft/vscode/issues/7827)
this._resetHandler(); // immediately stop this feature if the user starts to select (https://github.com/Microsoft/vscode/issues/7827)
}
}
private onEditorMouseMove(mouseEvent: ClickLinkMouseEvent): void {
this.lastMouseMoveEvent = mouseEvent;
private _onEditorMouseMove(mouseEvent: ClickLinkMouseEvent): void {
this._lastMouseMoveEvent = mouseEvent;
this._onMouseMoveOrRelevantKeyDown.fire([mouseEvent, null]);
}
private onEditorMouseDown(mouseEvent: ClickLinkMouseEvent): void {
private _onEditorMouseDown(mouseEvent: ClickLinkMouseEvent): void {
// We need to record if we had the trigger key on mouse down because someone might select something in the editor
// holding the mouse down and then while mouse is down start to press Ctrl/Cmd to start a copy operation and then
// release the mouse button without wanting to do the navigation.
// With this flag we prevent goto definition if the mouse was down before the trigger key was pressed.
this.hasTriggerKeyOnMouseDown = mouseEvent.hasTriggerModifier;
this._hasTriggerKeyOnMouseDown = mouseEvent.hasTriggerModifier;
this._lineNumberOnMouseDown = mouseEvent.target.position ? mouseEvent.target.position.lineNumber : 0;
}
private onEditorMouseUp(mouseEvent: ClickLinkMouseEvent): void {
if (this.hasTriggerKeyOnMouseDown) {
private _onEditorMouseUp(mouseEvent: ClickLinkMouseEvent): void {
const currentLineNumber = mouseEvent.target.position ? mouseEvent.target.position.lineNumber : 0;
if (this._hasTriggerKeyOnMouseDown && this._lineNumberOnMouseDown && this._lineNumberOnMouseDown === currentLineNumber) {
this._onExecute.fire(mouseEvent);
}
}
private onEditorKeyDown(e: ClickLinkKeyboardEvent): void {
private _onEditorKeyDown(e: ClickLinkKeyboardEvent): void {
if (
this.lastMouseMoveEvent
this._lastMouseMoveEvent
&& (
e.keyCodeIsTriggerKey // User just pressed Ctrl/Cmd (normal goto definition)
|| (e.keyCodeIsSideBySideKey && e.hasTriggerModifier) // User pressed Ctrl/Cmd+Alt (goto definition to the side)
)
) {
this._onMouseMoveOrRelevantKeyDown.fire([this.lastMouseMoveEvent, e]);
this._onMouseMoveOrRelevantKeyDown.fire([this._lastMouseMoveEvent, e]);
} else if (e.hasTriggerModifier) {
this._onCancel.fire(); // remove decorations if user holds another key with ctrl/cmd to prevent accident goto declaration
}
}
private onEditorKeyUp(e: ClickLinkKeyboardEvent): void {
private _onEditorKeyUp(e: ClickLinkKeyboardEvent): void {
if (e.keyCodeIsTriggerKey) {
this._onCancel.fire();
}
}
private resetHandler(): void {
this.lastMouseMoveEvent = null;
this.hasTriggerKeyOnMouseDown = false;
private _resetHandler(): void {
this._lastMouseMoveEvent = null;
this._hasTriggerKeyOnMouseDown = false;
this._onCancel.fire();
}
}

View file

@ -5044,6 +5044,28 @@ suite('autoClosingPairs', () => {
mode.dispose();
});
test('issue #90016: allow accents on mac US intl keyboard to surround selection', () => {
let mode = new AutoClosingMode();
usingCursor({
text: [
'test'
],
languageIdentifier: mode.getLanguageIdentifier()
}, (model, cursor) => {
cursor.setSelections('test', [new Selection(1, 1, 1, 5)]);
// Typing ` + e on the mac US intl kb layout
cursorCommand(cursor, H.CompositionStart, null, 'keyboard');
cursorCommand(cursor, H.Type, { text: '\'' }, 'keyboard');
cursorCommand(cursor, H.ReplacePreviousChar, { replaceCharCnt: 1, text: '\'' }, 'keyboard');
cursorCommand(cursor, H.ReplacePreviousChar, { replaceCharCnt: 1, text: '\'' }, 'keyboard');
cursorCommand(cursor, H.CompositionEnd, null, 'keyboard');
assert.equal(model.getValue(), '\'test\'');
});
mode.dispose();
});
test('issue #53357: Over typing ignores characters after backslash', () => {
let mode = new AutoClosingMode();
usingCursor({

View file

@ -7,11 +7,10 @@ import { Disposable } from 'vs/base/common/lifecycle';
import { IFileService, IFileContent, FileChangesEvent, FileSystemProviderError, FileSystemProviderErrorCode, FileOperationResult, FileOperationError } from 'vs/platform/files/common/files';
import { VSBuffer } from 'vs/base/common/buffer';
import { URI } from 'vs/base/common/uri';
import { SyncSource, SyncStatus, IUserData, IUserDataSyncStoreService, UserDataSyncErrorCode, UserDataSyncError, IUserDataSyncLogService, IUserDataSyncUtilService, ResourceKey, IUserDataSyncEnablementService } from 'vs/platform/userDataSync/common/userDataSync';
import { SyncSource, SyncStatus, IUserData, IUserDataSyncStoreService, UserDataSyncErrorCode, UserDataSyncError, IUserDataSyncLogService, IUserDataSyncUtilService, ResourceKey, IUserDataSyncEnablementService, IUserDataSyncBackupStoreService } from 'vs/platform/userDataSync/common/userDataSync';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { joinPath, dirname } from 'vs/base/common/resources';
import { toLocalISOString } from 'vs/base/common/date';
import { ThrottledDelayer, CancelablePromise } from 'vs/base/common/async';
import { CancelablePromise } from 'vs/base/common/async';
import { Emitter, Event } from 'vs/base/common/event';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { ParseError, parse } from 'vs/base/common/json';
@ -44,7 +43,6 @@ function isSyncData(thing: any): thing is ISyncData {
export abstract class AbstractSynchroniser extends Disposable {
protected readonly syncFolder: URI;
private cleanUpDelayer: ThrottledDelayer<void>;
private _status: SyncStatus = SyncStatus.Idle;
get status(): SyncStatus { return this._status; }
@ -58,9 +56,11 @@ export abstract class AbstractSynchroniser extends Disposable {
constructor(
readonly source: SyncSource,
readonly resourceKey: ResourceKey,
@IFileService protected readonly fileService: IFileService,
@IEnvironmentService environmentService: IEnvironmentService,
@IUserDataSyncStoreService protected readonly userDataSyncStoreService: IUserDataSyncStoreService,
@IUserDataSyncBackupStoreService protected readonly userDataSyncBackupStoreService: IUserDataSyncBackupStoreService,
@IUserDataSyncEnablementService protected readonly userDataSyncEnablementService: IUserDataSyncEnablementService,
@ITelemetryService private readonly telemetryService: ITelemetryService,
@IUserDataSyncLogService protected readonly logService: IUserDataSyncLogService,
@ -68,9 +68,7 @@ export abstract class AbstractSynchroniser extends Disposable {
) {
super();
this.syncFolder = joinPath(environmentService.userDataSyncHome, source);
this.lastSyncResource = joinPath(this.syncFolder, `lastSync${source}.json`);
this.cleanUpDelayer = new ThrottledDelayer(50);
this.cleanUpBackup();
this.lastSyncResource = joinPath(this.syncFolder, `lastSync${this.resourceKey}.json`);
}
protected setStatus(status: SyncStatus): void {
@ -217,51 +215,11 @@ export abstract class AbstractSynchroniser extends Disposable {
return { ref, syncData };
}
protected async backupLocal(content: VSBuffer): Promise<void> {
const resource = joinPath(this.syncFolder, `${toLocalISOString(new Date()).replace(/-|:|\.\d+Z$/g, '')}.json`);
try {
await this.fileService.writeFile(resource, content);
} catch (e) {
this.logService.error(e);
}
this.cleanUpDelayer.trigger(() => this.cleanUpBackup());
protected async backupLocal(content: string): Promise<void> {
const syncData: ISyncData = { version: this.version, content };
return this.userDataSyncBackupStoreService.backup(this.resourceKey, JSON.stringify(syncData));
}
private async cleanUpBackup(): Promise<void> {
try {
if (!(await this.fileService.exists(this.syncFolder))) {
return;
}
const stat = await this.fileService.resolve(this.syncFolder);
if (stat.children) {
const all = stat.children.filter(stat => stat.isFile && /^\d{8}T\d{6}(\.json)?$/.test(stat.name)).sort();
const backUpMaxAge = 1000 * 60 * 60 * 24 * (this.configurationService.getValue<number>('sync.localBackupDuration') || 30 /* Default 30 days */);
let toDelete = all.filter(stat => {
const ctime = stat.ctime || new Date(
parseInt(stat.name.substring(0, 4)),
parseInt(stat.name.substring(4, 6)) - 1,
parseInt(stat.name.substring(6, 8)),
parseInt(stat.name.substring(9, 11)),
parseInt(stat.name.substring(11, 13)),
parseInt(stat.name.substring(13, 15))
).getTime();
return Date.now() - ctime > backUpMaxAge;
});
const remaining = all.length - toDelete.length;
if (remaining < 10) {
toDelete = toDelete.slice(10 - remaining);
}
await Promise.all(toDelete.map(stat => {
this.logService.info('Deleting from backup', stat.resource.path);
this.fileService.del(stat.resource);
}));
}
} catch (e) {
this.logService.error(e);
}
}
abstract readonly resourceKey: ResourceKey;
protected abstract readonly version: number;
protected abstract performSync(remoteUserData: IRemoteUserData, lastSyncUserData: IRemoteUserData | null): Promise<SyncStatus>;
}
@ -283,15 +241,17 @@ export abstract class AbstractFileSynchroniser extends AbstractSynchroniser {
constructor(
protected readonly file: URI,
source: SyncSource,
resourceKey: ResourceKey,
@IFileService fileService: IFileService,
@IEnvironmentService environmentService: IEnvironmentService,
@IUserDataSyncStoreService userDataSyncStoreService: IUserDataSyncStoreService,
@IUserDataSyncBackupStoreService userDataSyncBackupStoreService: IUserDataSyncBackupStoreService,
@IUserDataSyncEnablementService userDataSyncEnablementService: IUserDataSyncEnablementService,
@ITelemetryService telemetryService: ITelemetryService,
@IUserDataSyncLogService logService: IUserDataSyncLogService,
@IConfigurationService configurationService: IConfigurationService,
) {
super(source, fileService, environmentService, userDataSyncStoreService, userDataSyncEnablementService, telemetryService, logService, configurationService);
super(source, resourceKey, fileService, environmentService, userDataSyncStoreService, userDataSyncBackupStoreService, userDataSyncEnablementService, telemetryService, logService, configurationService);
this._register(this.fileService.watch(dirname(file)));
this._register(this.fileService.onDidFilesChange(e => this.onFileChanges(e)));
}
@ -327,7 +287,6 @@ export abstract class AbstractFileSynchroniser extends AbstractSynchroniser {
try {
if (oldContent) {
// file exists already
await this.backupLocal(oldContent.value);
await this.fileService.writeFile(this.file, VSBuffer.fromString(newContent), oldContent);
} else {
// file does not exist
@ -382,16 +341,18 @@ export abstract class AbstractJsonFileSynchroniser extends AbstractFileSynchroni
constructor(
file: URI,
source: SyncSource,
resourceKey: ResourceKey,
@IFileService fileService: IFileService,
@IEnvironmentService environmentService: IEnvironmentService,
@IUserDataSyncStoreService userDataSyncStoreService: IUserDataSyncStoreService,
@IUserDataSyncBackupStoreService userDataSyncBackupStoreService: IUserDataSyncBackupStoreService,
@IUserDataSyncEnablementService userDataSyncEnablementService: IUserDataSyncEnablementService,
@ITelemetryService telemetryService: ITelemetryService,
@IUserDataSyncLogService logService: IUserDataSyncLogService,
@IUserDataSyncUtilService protected readonly userDataSyncUtilService: IUserDataSyncUtilService,
@IConfigurationService configurationService: IConfigurationService,
) {
super(file, source, fileService, environmentService, userDataSyncStoreService, userDataSyncEnablementService, telemetryService, logService, configurationService);
super(file, source, resourceKey, fileService, environmentService, userDataSyncStoreService, userDataSyncBackupStoreService, userDataSyncEnablementService, telemetryService, logService, configurationService);
}
protected hasErrors(content: string): boolean {

View file

@ -3,7 +3,7 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { SyncStatus, IUserDataSyncStoreService, ISyncExtension, IUserDataSyncLogService, IUserDataSynchroniser, SyncSource, ResourceKey, IUserDataSyncEnablementService } from 'vs/platform/userDataSync/common/userDataSync';
import { SyncStatus, IUserDataSyncStoreService, ISyncExtension, IUserDataSyncLogService, IUserDataSynchroniser, SyncSource, IUserDataSyncEnablementService, IUserDataSyncBackupStoreService } from 'vs/platform/userDataSync/common/userDataSync';
import { Event } from 'vs/base/common/event';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { IExtensionManagementService, IExtensionGalleryService, IGlobalExtensionEnablementService } from 'vs/platform/extensionManagement/common/extensionManagement';
@ -16,7 +16,6 @@ import { merge } from 'vs/platform/userDataSync/common/extensionsMerge';
import { isNonEmptyArray } from 'vs/base/common/arrays';
import { AbstractSynchroniser, IRemoteUserData, ISyncData } from 'vs/platform/userDataSync/common/abstractSynchronizer';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { VSBuffer } from 'vs/base/common/buffer';
interface ISyncPreviewResult {
readonly localExtensions: ISyncExtension[];
@ -35,7 +34,6 @@ interface ILastSyncUserData extends IRemoteUserData {
export class ExtensionsSynchroniser extends AbstractSynchroniser implements IUserDataSynchroniser {
readonly resourceKey: ResourceKey = 'extensions';
protected readonly version: number = 2;
protected isEnabled(): boolean { return super.isEnabled() && this.extensionGalleryService.isEnabled(); }
@ -43,6 +41,7 @@ export class ExtensionsSynchroniser extends AbstractSynchroniser implements IUse
@IEnvironmentService environmentService: IEnvironmentService,
@IFileService fileService: IFileService,
@IUserDataSyncStoreService userDataSyncStoreService: IUserDataSyncStoreService,
@IUserDataSyncBackupStoreService userDataSyncBackupStoreService: IUserDataSyncBackupStoreService,
@IExtensionManagementService private readonly extensionManagementService: IExtensionManagementService,
@IGlobalExtensionEnablementService private readonly extensionEnablementService: IGlobalExtensionEnablementService,
@IUserDataSyncLogService logService: IUserDataSyncLogService,
@ -51,7 +50,7 @@ export class ExtensionsSynchroniser extends AbstractSynchroniser implements IUse
@IUserDataSyncEnablementService userDataSyncEnablementService: IUserDataSyncEnablementService,
@ITelemetryService telemetryService: ITelemetryService,
) {
super(SyncSource.Extensions, fileService, environmentService, userDataSyncStoreService, userDataSyncEnablementService, telemetryService, logService, configurationService);
super(SyncSource.Extensions, 'extensions', fileService, environmentService, userDataSyncStoreService, userDataSyncBackupStoreService, userDataSyncEnablementService, telemetryService, logService, configurationService);
this._register(
Event.debounce(
Event.any<any>(
@ -180,7 +179,7 @@ export class ExtensionsSynchroniser extends AbstractSynchroniser implements IUse
if (added.length || removed.length || updated.length) {
// back up all disabled or market place extensions
const backUpExtensions = localExtensions.filter(e => e.disabled || !!e.identifier.uuid);
await this.backupLocal(VSBuffer.fromString(JSON.stringify(backUpExtensions, null, '\t')));
await this.backupLocal(JSON.stringify(backUpExtensions));
skippedExtensions = await this.updateLocalExtensions(added, removed, updated, skippedExtensions);
}

View file

@ -3,7 +3,7 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { SyncStatus, IUserDataSyncStoreService, IUserDataSyncLogService, IGlobalState, SyncSource, IUserDataSynchroniser, ResourceKey, IUserDataSyncEnablementService } from 'vs/platform/userDataSync/common/userDataSync';
import { SyncStatus, IUserDataSyncStoreService, IUserDataSyncLogService, IGlobalState, SyncSource, IUserDataSynchroniser, IUserDataSyncEnablementService, IUserDataSyncBackupStoreService } from 'vs/platform/userDataSync/common/userDataSync';
import { VSBuffer } from 'vs/base/common/buffer';
import { Event } from 'vs/base/common/event';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
@ -29,19 +29,19 @@ interface ISyncPreviewResult {
export class GlobalStateSynchroniser extends AbstractSynchroniser implements IUserDataSynchroniser {
readonly resourceKey: ResourceKey = 'globalState';
protected readonly version: number = 1;
constructor(
@IFileService fileService: IFileService,
@IUserDataSyncStoreService userDataSyncStoreService: IUserDataSyncStoreService,
@IUserDataSyncBackupStoreService userDataSyncBackupStoreService: IUserDataSyncBackupStoreService,
@IUserDataSyncLogService logService: IUserDataSyncLogService,
@IEnvironmentService private readonly environmentService: IEnvironmentService,
@IUserDataSyncEnablementService userDataSyncEnablementService: IUserDataSyncEnablementService,
@ITelemetryService telemetryService: ITelemetryService,
@IConfigurationService configurationService: IConfigurationService,
) {
super(SyncSource.GlobalState, fileService, environmentService, userDataSyncStoreService, userDataSyncEnablementService, telemetryService, logService, configurationService);
super(SyncSource.GlobalState, 'globalState', fileService, environmentService, userDataSyncStoreService, userDataSyncBackupStoreService, userDataSyncEnablementService, telemetryService, logService, configurationService);
this._register(this.fileService.watch(dirname(this.environmentService.argvResource)));
this._register(Event.filter(this.fileService.onDidFilesChange, e => e.contains(this.environmentService.argvResource))(() => this._onDidChangeLocal.fire()));
}
@ -130,7 +130,7 @@ export class GlobalStateSynchroniser extends AbstractSynchroniser implements IUs
return SyncStatus.Idle;
}
private async getPreview(remoteUserData: IRemoteUserData, lastSyncUserData: IRemoteUserData | null,): Promise<ISyncPreviewResult> {
private async getPreview(remoteUserData: IRemoteUserData, lastSyncUserData: IRemoteUserData | null): Promise<ISyncPreviewResult> {
const remoteGlobalState: IGlobalState = remoteUserData.syncData ? JSON.parse(remoteUserData.syncData.content) : null;
const lastSyncGlobalState = lastSyncUserData && lastSyncUserData.syncData ? JSON.parse(lastSyncUserData.syncData.content) : null;
@ -158,7 +158,7 @@ export class GlobalStateSynchroniser extends AbstractSynchroniser implements IUs
if (local) {
// update local
this.logService.trace('UI State: Updating local ui state...');
await this.backupLocal(VSBuffer.fromString(JSON.stringify(localUserData, null, '\t')));
await this.backupLocal(JSON.stringify(localUserData));
await this.writeLocalGlobalState(local);
this.logService.info('UI State: Updated local ui state');
}

View file

@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/
import { IFileService, FileOperationError, FileOperationResult } from 'vs/platform/files/common/files';
import { UserDataSyncError, UserDataSyncErrorCode, SyncStatus, IUserDataSyncStoreService, IUserDataSyncLogService, IUserDataSyncUtilService, SyncSource, IUserDataSynchroniser, ResourceKey, IUserDataSyncEnablementService } from 'vs/platform/userDataSync/common/userDataSync';
import { UserDataSyncError, UserDataSyncErrorCode, SyncStatus, IUserDataSyncStoreService, IUserDataSyncLogService, IUserDataSyncUtilService, SyncSource, IUserDataSynchroniser, IUserDataSyncEnablementService, IUserDataSyncBackupStoreService } from 'vs/platform/userDataSync/common/userDataSync';
import { merge } from 'vs/platform/userDataSync/common/keybindingsMerge';
import { VSBuffer } from 'vs/base/common/buffer';
import { parse } from 'vs/base/common/json';
@ -29,12 +29,12 @@ interface ISyncContent {
export class KeybindingsSynchroniser extends AbstractJsonFileSynchroniser implements IUserDataSynchroniser {
readonly resourceKey: ResourceKey = 'keybindings';
protected get conflictsPreviewResource(): URI { return this.environmentService.keybindingsSyncPreviewResource; }
protected readonly version: number = 1;
constructor(
@IUserDataSyncStoreService userDataSyncStoreService: IUserDataSyncStoreService,
@IUserDataSyncBackupStoreService userDataSyncBackupStoreService: IUserDataSyncBackupStoreService,
@IUserDataSyncLogService logService: IUserDataSyncLogService,
@IConfigurationService configurationService: IConfigurationService,
@IUserDataSyncEnablementService userDataSyncEnablementService: IUserDataSyncEnablementService,
@ -43,7 +43,7 @@ export class KeybindingsSynchroniser extends AbstractJsonFileSynchroniser implem
@IUserDataSyncUtilService userDataSyncUtilService: IUserDataSyncUtilService,
@ITelemetryService telemetryService: ITelemetryService,
) {
super(environmentService.keybindingsResource, SyncSource.Keybindings, fileService, environmentService, userDataSyncStoreService, userDataSyncEnablementService, telemetryService, logService, userDataSyncUtilService, configurationService);
super(environmentService.keybindingsResource, SyncSource.Keybindings, 'keybindings', fileService, environmentService, userDataSyncStoreService, userDataSyncBackupStoreService, userDataSyncEnablementService, telemetryService, logService, userDataSyncUtilService, configurationService);
}
async pull(): Promise<void> {
@ -197,13 +197,14 @@ export class KeybindingsSynchroniser extends AbstractJsonFileSynchroniser implem
if (hasLocalChanged) {
this.logService.trace('Keybindings: Updating local keybindings...');
await this.backupLocal(this.toSyncContent(content, null));
await this.updateLocalFileContent(content, fileContent);
this.logService.info('Keybindings: Updated local keybindings');
}
if (hasRemoteChanged) {
this.logService.trace('Keybindings: Updating remote keybindings...');
const remoteContents = this.updateSyncContent(content, remoteUserData.syncData ? remoteUserData.syncData.content : null);
const remoteContents = this.toSyncContent(content, remoteUserData.syncData ? remoteUserData.syncData.content : null);
remoteUserData = await this.updateRemoteUserData(remoteContents, forcePush ? null : remoteUserData.ref);
this.logService.info('Keybindings: Updated remote keybindings');
}
@ -218,7 +219,7 @@ export class KeybindingsSynchroniser extends AbstractJsonFileSynchroniser implem
if (lastSyncUserData?.ref !== remoteUserData.ref && (content !== null || fileContent !== null)) {
this.logService.trace('Keybindings: Updating last synchronized keybindings...');
const lastSyncContent = this.updateSyncContent(content !== null ? content : fileContent!.value.toString(), null);
const lastSyncContent = this.toSyncContent(content !== null ? content : fileContent!.value.toString(), null);
await this.updateLastSyncUserData({ ref: remoteUserData.ref, syncData: { version: remoteUserData.syncData!.version, content: lastSyncContent } });
this.logService.info('Keybindings: Updated last synchronized keybindings');
}
@ -301,7 +302,7 @@ export class KeybindingsSynchroniser extends AbstractJsonFileSynchroniser implem
}
}
private updateSyncContent(keybindingsContent: string, syncContent: string | null): string {
private toSyncContent(keybindingsContent: string, syncContent: string | null): string {
let parsed: ISyncContent = {};
try {
parsed = JSON.parse(syncContent || '{}');

View file

@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/
import { IFileService, FileOperationError, FileOperationResult } from 'vs/platform/files/common/files';
import { UserDataSyncError, UserDataSyncErrorCode, SyncStatus, IUserDataSyncStoreService, IUserDataSyncLogService, IUserDataSyncUtilService, IConflictSetting, ISettingsSyncService, CONFIGURATION_SYNC_STORE_KEY, SyncSource, ResourceKey, IUserDataSyncEnablementService } from 'vs/platform/userDataSync/common/userDataSync';
import { UserDataSyncError, UserDataSyncErrorCode, SyncStatus, IUserDataSyncStoreService, IUserDataSyncLogService, IUserDataSyncUtilService, IConflictSetting, ISettingsSyncService, CONFIGURATION_SYNC_STORE_KEY, SyncSource, IUserDataSyncEnablementService, IUserDataSyncBackupStoreService } from 'vs/platform/userDataSync/common/userDataSync';
import { VSBuffer } from 'vs/base/common/buffer';
import { parse } from 'vs/base/common/json';
import { localize } from 'vs/nls';
@ -37,7 +37,6 @@ export class SettingsSynchroniser extends AbstractJsonFileSynchroniser implement
_serviceBrand: any;
readonly resourceKey: ResourceKey = 'settings';
protected readonly version: number = 1;
protected get conflictsPreviewResource(): URI { return this.environmentService.settingsSyncPreviewResource; }
@ -50,6 +49,7 @@ export class SettingsSynchroniser extends AbstractJsonFileSynchroniser implement
@IFileService fileService: IFileService,
@IEnvironmentService private readonly environmentService: IEnvironmentService,
@IUserDataSyncStoreService userDataSyncStoreService: IUserDataSyncStoreService,
@IUserDataSyncBackupStoreService userDataSyncBackupStoreService: IUserDataSyncBackupStoreService,
@IUserDataSyncLogService logService: IUserDataSyncLogService,
@IUserDataSyncUtilService userDataSyncUtilService: IUserDataSyncUtilService,
@IConfigurationService configurationService: IConfigurationService,
@ -57,7 +57,7 @@ export class SettingsSynchroniser extends AbstractJsonFileSynchroniser implement
@ITelemetryService telemetryService: ITelemetryService,
@IExtensionManagementService private readonly extensionManagementService: IExtensionManagementService,
) {
super(environmentService.settingsResource, SyncSource.Settings, fileService, environmentService, userDataSyncStoreService, userDataSyncEnablementService, telemetryService, logService, userDataSyncUtilService, configurationService);
super(environmentService.settingsResource, SyncSource.Settings, 'settings', fileService, environmentService, userDataSyncStoreService, userDataSyncBackupStoreService, userDataSyncEnablementService, telemetryService, logService, userDataSyncUtilService, configurationService);
}
protected setStatus(status: SyncStatus): void {
@ -259,6 +259,7 @@ export class SettingsSynchroniser extends AbstractJsonFileSynchroniser implement
if (hasLocalChanged) {
this.logService.trace('Settings: Updating local settings...');
await this.backupLocal(JSON.stringify(this.toSettingsSyncContent(content)));
await this.updateLocalFileContent(content, fileContent);
this.logService.info('Settings: Updated local settings');
}
@ -269,7 +270,7 @@ export class SettingsSynchroniser extends AbstractJsonFileSynchroniser implement
const ignoredSettings = await this.getIgnoredSettings(content);
content = updateIgnoredSettings(content, remoteSettingsSyncContent ? remoteSettingsSyncContent.settings : '{}', ignoredSettings, formatUtils);
this.logService.trace('Settings: Updating remote settings...');
remoteUserData = await this.updateRemoteUserData(JSON.stringify(<ISettingsSyncContent>{ settings: content }), forcePush ? null : remoteUserData.ref);
remoteUserData = await this.updateRemoteUserData(JSON.stringify(this.toSettingsSyncContent(content)), forcePush ? null : remoteUserData.ref);
this.logService.info('Settings: Updated remote settings');
}
@ -354,6 +355,10 @@ export class SettingsSynchroniser extends AbstractJsonFileSynchroniser implement
return null;
}
private toSettingsSyncContent(settings: string): ISettingsSyncContent {
return { settings };
}
private _defaultIgnoredSettings: Promise<string[]> | undefined = undefined;
protected async getIgnoredSettings(content?: string): Promise<string[]> {
if (!this._defaultIgnoredSettings) {

View file

@ -156,6 +156,12 @@ export interface IUserDataSyncStoreService {
delete(key: ResourceKey): Promise<void>;
}
export const IUserDataSyncBackupStoreService = createDecorator<IUserDataSyncBackupStoreService>('IUserDataSyncBackupStoreService');
export interface IUserDataSyncBackupStoreService {
_serviceBrand: undefined;
backup(resourceKey: ResourceKey, content: string): Promise<void>;
}
//#endregion
// #region User Data Sync Error

View file

@ -0,0 +1,80 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { Disposable, } from 'vs/base/common/lifecycle';
import { IUserDataSyncLogService, ResourceKey, ALL_RESOURCE_KEYS, IUserDataSyncBackupStoreService } from 'vs/platform/userDataSync/common/userDataSync';
import { joinPath } from 'vs/base/common/resources';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { IFileService } from 'vs/platform/files/common/files';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { toLocalISOString } from 'vs/base/common/date';
import { VSBuffer } from 'vs/base/common/buffer';
export class UserDataSyncBackupStoreService extends Disposable implements IUserDataSyncBackupStoreService {
_serviceBrand: any;
constructor(
@IEnvironmentService private readonly environmentService: IEnvironmentService,
@IFileService private readonly fileService: IFileService,
@IConfigurationService private readonly configurationService: IConfigurationService,
@IUserDataSyncLogService private readonly logService: IUserDataSyncLogService,
) {
super();
ALL_RESOURCE_KEYS.forEach(resourceKey => this.cleanUpBackup(resourceKey));
}
async backup(resourceKey: ResourceKey, content: string): Promise<void> {
const folder = joinPath(this.environmentService.userDataSyncHome, resourceKey);
const resource = joinPath(folder, `${toLocalISOString(new Date()).replace(/-|:|\.\d+Z$/g, '')}.json`);
try {
await this.fileService.writeFile(resource, VSBuffer.fromString(content));
} catch (e) {
this.logService.error(e);
}
try {
this.cleanUpBackup(resourceKey);
} catch (e) { /* Ignore */ }
}
private async cleanUpBackup(resourceKey: ResourceKey): Promise<void> {
const folder = joinPath(this.environmentService.userDataSyncHome, resourceKey);
try {
try {
if (!(await this.fileService.exists(folder))) {
return;
}
} catch (e) {
return;
}
const stat = await this.fileService.resolve(folder);
if (stat.children) {
const all = stat.children.filter(stat => stat.isFile && /^\d{8}T\d{6}(\.json)?$/.test(stat.name)).sort();
const backUpMaxAge = 1000 * 60 * 60 * 24 * (this.configurationService.getValue<number>('sync.localBackupDuration') || 30 /* Default 30 days */);
let toDelete = all.filter(stat => {
const ctime = stat.ctime || new Date(
parseInt(stat.name.substring(0, 4)),
parseInt(stat.name.substring(4, 6)) - 1,
parseInt(stat.name.substring(6, 8)),
parseInt(stat.name.substring(9, 11)),
parseInt(stat.name.substring(11, 13)),
parseInt(stat.name.substring(13, 15))
).getTime();
return Date.now() - ctime > backUpMaxAge;
});
const remaining = all.length - toDelete.length;
if (remaining < 10) {
toDelete = toDelete.slice(10 - remaining);
}
await Promise.all(toDelete.map(stat => {
this.logService.info('Deleting from backup', stat.resource.path);
this.fileService.del(stat.resource);
}));
}
} catch (e) {
this.logService.error(e);
}
}
}

View file

@ -6,7 +6,7 @@
import { IRequestService } from 'vs/platform/request/common/request';
import { IRequestOptions, IRequestContext, IHeaders } from 'vs/base/parts/request/common/request';
import { CancellationToken } from 'vs/base/common/cancellation';
import { IUserData, ResourceKey, IUserDataManifest, ALL_RESOURCE_KEYS, IUserDataSyncLogService, IUserDataSyncStoreService, IUserDataSyncUtilService, IUserDataSyncEnablementService, ISettingsSyncService, IUserDataSyncService, getDefaultIgnoredSettings } from 'vs/platform/userDataSync/common/userDataSync';
import { IUserData, ResourceKey, IUserDataManifest, ALL_RESOURCE_KEYS, IUserDataSyncLogService, IUserDataSyncStoreService, IUserDataSyncUtilService, IUserDataSyncEnablementService, ISettingsSyncService, IUserDataSyncService, getDefaultIgnoredSettings, IUserDataSyncBackupStoreService } from 'vs/platform/userDataSync/common/userDataSync';
import { bufferToStream, VSBuffer } from 'vs/base/common/buffer';
import { generateUuid } from 'vs/base/common/uuid';
import { UserDataSyncService } from 'vs/platform/userDataSync/common/userDataSyncService';
@ -36,6 +36,7 @@ import { Emitter } from 'vs/base/common/event';
import { IAuthenticationTokenService } from 'vs/platform/authentication/common/authentication';
import product from 'vs/platform/product/common/product';
import { IProductService } from 'vs/platform/product/common/productService';
import { UserDataSyncBackupStoreService } from 'vs/platform/userDataSync/common/userDataSyncBackupStoreService';
export class UserDataSyncClient extends Disposable {
@ -89,6 +90,7 @@ export class UserDataSyncClient extends Disposable {
this.instantiationService.stub(IUserDataSyncLogService, logService);
this.instantiationService.stub(ITelemetryService, NullTelemetryService);
this.instantiationService.stub(IUserDataSyncStoreService, this.instantiationService.createInstance(UserDataSyncStoreService));
this.instantiationService.stub(IUserDataSyncBackupStoreService, this.instantiationService.createInstance(UserDataSyncBackupStoreService));
this.instantiationService.stub(IUserDataSyncUtilService, new TestUserDataSyncUtilService());
this.instantiationService.stub(IUserDataSyncEnablementService, this.instantiationService.createInstance(UserDataSyncEnablementService));

View file

@ -1745,4 +1745,18 @@ declare module 'vscode' {
}
//#endregion
//#region https://github.com/microsoft/vscode/issues/90517
export interface FileSystemError {
/**
* A code that identifies this error.
*
* Possible values are names of errors, like [`FileNotFound`](#FileSystemError.FileNotFound),
* or `undefined` for an unspecified error.
*/
readonly code?: string;
}
////#endregion
}

View file

@ -33,6 +33,7 @@ import {
RunOptionsDTO
} from 'vs/workbench/api/common/shared/tasks';
import { IConfigurationResolverService } from 'vs/workbench/services/configurationResolver/common/configurationResolver';
import { ConfigurationTarget } from 'vs/platform/configuration/common/configuration';
namespace TaskExecutionDTO {
export function from(value: TaskExecution): TaskExecutionDTO {
@ -604,7 +605,7 @@ export class MainThreadTask implements MainThreadTaskShape {
return URI.parse(`${info.scheme}://${info.authority}${path}`);
},
context: this._extHostContext,
resolveVariables: (workspaceFolder: IWorkspaceFolder, toResolve: ResolveSet): Promise<ResolvedVariables> => {
resolveVariables: (workspaceFolder: IWorkspaceFolder, toResolve: ResolveSet, target: ConfigurationTarget): Promise<ResolvedVariables> => {
const vars: string[] = [];
toResolve.variables.forEach(item => vars.push(item));
return Promise.resolve(this._proxy.$resolveVariables(workspaceFolder.uri, { process: toResolve.process, variables: vars })).then(values => {
@ -613,7 +614,7 @@ export class MainThreadTask implements MainThreadTaskShape {
partiallyResolvedVars.push(entry.value);
});
return new Promise<ResolvedVariables>((resolve, reject) => {
this._configurationResolverService.resolveWithInteraction(workspaceFolder, partiallyResolvedVars, 'tasks').then(resolvedVars => {
this._configurationResolverService.resolveWithInteraction(workspaceFolder, partiallyResolvedVars, 'tasks', undefined, target).then(resolvedVars => {
const result: ResolvedVariables = {
process: undefined,
variables: new Map<string, string>()

View file

@ -252,7 +252,15 @@ export class ExtensionsActivator {
return;
}
const currentExtension = this._registry.getExtensionDescription(currentActivation.id)!;
const currentExtension = this._registry.getExtensionDescription(currentActivation.id);
if (!currentExtension) {
// Error condition 0: unknown extension
this._host.onExtensionActivationError(currentActivation.id, new MissingDependencyError(currentActivation.id.value));
const error = new Error(`Unknown dependency '${currentActivation.id.value}'`);
this._activatedExtensions.set(ExtensionIdentifier.toKey(currentActivation.id), new FailedExtension(error));
return;
}
const depIds = (typeof currentExtension.extensionDependencies === 'undefined' ? [] : currentExtension.extensionDependencies);
let currentExtensionGetsGreenLight = true;

View file

@ -148,7 +148,16 @@ class ConsumerFileSystem implements vscode.FileSystem {
}
// file system error
throw new FileSystemError(err.message, err.name as files.FileSystemProviderErrorCode);
switch (err.name) {
case files.FileSystemProviderErrorCode.FileExists: throw FileSystemError.FileExists(err.message);
case files.FileSystemProviderErrorCode.FileNotFound: throw FileSystemError.FileNotFound(err.message);
case files.FileSystemProviderErrorCode.FileNotADirectory: throw FileSystemError.FileNotADirectory(err.message);
case files.FileSystemProviderErrorCode.FileIsADirectory: throw FileSystemError.FileIsADirectory(err.message);
case files.FileSystemProviderErrorCode.NoPermissions: throw FileSystemError.NoPermissions(err.message);
case files.FileSystemProviderErrorCode.Unavailable: throw FileSystemError.Unavailable(err.message);
default: throw new FileSystemError(err.message, err.name as files.FileSystemProviderErrorCode);
}
}
}

View file

@ -2333,9 +2333,13 @@ export class FileSystemError extends Error {
return new FileSystemError(messageOrUri, FileSystemProviderErrorCode.Unavailable, FileSystemError.Unavailable);
}
readonly code?: string;
constructor(uriOrMessage?: string | URI, code: FileSystemProviderErrorCode = FileSystemProviderErrorCode.Unknown, terminator?: Function) {
super(URI.isUri(uriOrMessage) ? uriOrMessage.toString(true) : uriOrMessage);
this.code = terminator?.name;
// mark the error as file system provider error so that
// we can extract the error code on the receiving side
markAsFileSystemProviderError(this, code);

View file

@ -67,6 +67,7 @@ export class CustomTreeViewPane extends ViewPane {
this._register(this.treeView.onDidChangeTitle((newTitle) => this.updateTitle(newTitle)));
this._register(toDisposable(() => this.treeView.setVisibility(false)));
this._register(this.onDidChangeBodyVisibility(() => this.updateTreeVisibility()));
this._register(this.treeView.onDidChangeWelcomeState(() => this._onDidChangeViewWelcomeState.fire()));
this.updateTreeVisibility();
}
@ -83,6 +84,10 @@ export class CustomTreeViewPane extends ViewPane {
}
}
shouldShowWelcome(): boolean {
return (this.treeView.dataProvider === undefined) && (this.treeView.message === undefined);
}
layoutBody(height: number, width: number): void {
this.treeView.layout(height, width);
}
@ -151,6 +156,9 @@ export class CustomTreeView extends Disposable implements ITreeView {
private readonly _onDidChangeActions: Emitter<void> = this._register(new Emitter<void>());
readonly onDidChangeActions: Event<void> = this._onDidChangeActions.event;
private readonly _onDidChangeWelcomeState: Emitter<void> = this._register(new Emitter<void>());
readonly onDidChangeWelcomeState: Event<void> = this._onDidChangeWelcomeState.event;
private readonly _onDidChangeTitle: Emitter<string> = this._register(new Emitter<string>());
readonly onDidChangeTitle: Event<string> = this._onDidChangeTitle.event;
@ -223,6 +231,8 @@ export class CustomTreeView extends Disposable implements ITreeView {
this._dataProvider = undefined;
this.updateMessage();
}
this._onDidChangeWelcomeState.fire();
}
private _message: string | undefined;
@ -233,6 +243,7 @@ export class CustomTreeView extends Disposable implements ITreeView {
set message(message: string | undefined) {
this._message = message;
this.updateMessage();
this._onDidChangeWelcomeState.fire();
}
get title(): string {

View file

@ -488,6 +488,8 @@ export interface ITreeView extends IDisposable {
readonly onDidChangeTitle: Event<string>;
readonly onDidChangeWelcomeState: Event<void>;
refresh(treeItems?: ITreeItem[]): Promise<void>;
setVisibility(visible: boolean): void;

View file

@ -44,7 +44,7 @@ import { WatchExpressionsView } from 'vs/workbench/contrib/debug/browser/watchEx
import { VariablesView } from 'vs/workbench/contrib/debug/browser/variablesView';
import { ClearReplAction, Repl } from 'vs/workbench/contrib/debug/browser/repl';
import { DebugContentProvider } from 'vs/workbench/contrib/debug/common/debugContentProvider';
import { StartView } from 'vs/workbench/contrib/debug/browser/startView';
import { WelcomeView } from 'vs/workbench/contrib/debug/browser/welcomeView';
import { ThemeIcon } from 'vs/platform/theme/common/themeService';
import { DebugViewPaneContainer, OpenDebugPanelAction } from 'vs/workbench/contrib/debug/browser/debugViewlet';
import { registerEditorContribution } from 'vs/editor/browser/editorExtensions';
@ -108,7 +108,7 @@ viewsRegistry.registerViews([{ id: VARIABLES_VIEW_ID, name: nls.localize('variab
viewsRegistry.registerViews([{ id: WATCH_VIEW_ID, name: nls.localize('watch', "Watch"), ctorDescriptor: new SyncDescriptor(WatchExpressionsView), order: 20, weight: 10, canToggleVisibility: true, canMoveView: true, focusCommand: { id: 'workbench.debug.action.focusWatchView' }, when: CONTEXT_DEBUG_UX.isEqualTo('default') }], viewContainer);
viewsRegistry.registerViews([{ id: CALLSTACK_VIEW_ID, name: nls.localize('callStack', "Call Stack"), ctorDescriptor: new SyncDescriptor(CallStackView), order: 30, weight: 30, canToggleVisibility: true, canMoveView: true, focusCommand: { id: 'workbench.debug.action.focusCallStackView' }, when: CONTEXT_DEBUG_UX.isEqualTo('default') }], viewContainer);
viewsRegistry.registerViews([{ id: BREAKPOINTS_VIEW_ID, name: nls.localize('breakpoints', "Breakpoints"), ctorDescriptor: new SyncDescriptor(BreakpointsView), order: 40, weight: 20, canToggleVisibility: true, canMoveView: true, focusCommand: { id: 'workbench.debug.action.focusBreakpointsView' }, when: ContextKeyExpr.or(CONTEXT_BREAKPOINTS_EXIST, CONTEXT_DEBUG_UX.isEqualTo('default')) }], viewContainer);
viewsRegistry.registerViews([{ id: StartView.ID, name: StartView.LABEL, ctorDescriptor: new SyncDescriptor(StartView), order: 10, weight: 40, canToggleVisibility: true, when: CONTEXT_DEBUG_UX.isEqualTo('simple') }], viewContainer);
viewsRegistry.registerViews([{ id: WelcomeView.ID, name: WelcomeView.LABEL, ctorDescriptor: new SyncDescriptor(WelcomeView), order: 10, weight: 40, canToggleVisibility: true, when: CONTEXT_DEBUG_UX.isEqualTo('simple') }], viewContainer);
viewsRegistry.registerViews([{ id: LOADED_SCRIPTS_VIEW_ID, name: nls.localize('loadedScripts', "Loaded Scripts"), ctorDescriptor: new SyncDescriptor(LoadedScriptsView), order: 35, weight: 5, canToggleVisibility: true, canMoveView: true, collapsed: true, when: ContextKeyExpr.and(CONTEXT_LOADED_SCRIPTS_SUPPORTED, CONTEXT_DEBUG_UX.isEqualTo('default')) }], viewContainer);
registerCommands();

View file

@ -32,8 +32,8 @@ import { MenuEntryActionViewItem } from 'vs/platform/actions/browser/menuEntryAc
import { INotificationService } from 'vs/platform/notification/common/notification';
import { TogglePanelAction } from 'vs/workbench/browser/panel';
import { IPanelService } from 'vs/workbench/services/panel/common/panelService';
import { StartView } from 'vs/workbench/contrib/debug/browser/startView';
import { IViewDescriptorService } from 'vs/workbench/common/views';
import { WelcomeView } from 'vs/workbench/contrib/debug/browser/welcomeView';
export class DebugViewPaneContainer extends ViewPaneContainer {
@ -92,7 +92,7 @@ export class DebugViewPaneContainer extends ViewPaneContainer {
if (this.startDebugActionViewItem) {
this.startDebugActionViewItem.focus();
} else {
this.focusView(StartView.ID);
this.focusView(WelcomeView.ID);
}
}

View file

@ -29,10 +29,10 @@ const debugStartLanguageKey = 'debugStartLanguage';
const CONTEXT_DEBUG_START_LANGUAGE = new RawContextKey<string>(debugStartLanguageKey, undefined);
const CONTEXT_DEBUGGER_INTERESTED_IN_ACTIVE_EDITOR = new RawContextKey<boolean>('debuggerInterestedInActiveEditor', false);
export class StartView extends ViewPane {
export class WelcomeView extends ViewPane {
static ID = 'workbench.debug.startView';
static LABEL = localize('start', "Start");
static ID = 'workbench.debug.welcome';
static LABEL = localize('run', "Run");
private debugStartLanguageContext: IContextKey<string | undefined>;
private debuggerInterestedContext: IContextKey<boolean>;
@ -52,7 +52,7 @@ export class StartView extends ViewPane {
@IStorageService storageSevice: IStorageService,
@ITelemetryService telemetryService: ITelemetryService,
) {
super({ ...(options as IViewPaneOptions), ariaHeaderLabel: localize('debugStart', "Debug Start Section") }, keybindingService, contextMenuService, configurationService, contextKeyService, viewDescriptorService, instantiationService, openerService, themeService, telemetryService);
super({ ...(options as IViewPaneOptions), ariaHeaderLabel: localize('debugStart', "Debug Welcome Section") }, keybindingService, contextMenuService, configurationService, contextKeyService, viewDescriptorService, instantiationService, openerService, themeService, telemetryService);
this.debugStartLanguageContext = CONTEXT_DEBUG_START_LANGUAGE.bindTo(contextKeyService);
this.debuggerInterestedContext = CONTEXT_DEBUGGER_INTERESTED_IN_ACTIVE_EDITOR.bindTo(contextKeyService);
@ -87,23 +87,23 @@ export class StartView extends ViewPane {
}
const viewsRegistry = Registry.as<IViewsRegistry>(Extensions.ViewsRegistry);
viewsRegistry.registerViewWelcomeContent(StartView.ID, {
viewsRegistry.registerViewWelcomeContent(WelcomeView.ID, {
content: localize('openAFileWhichCanBeDebugged', "[Open a file](command:{0}) which can be debugged or run.", isMacintosh ? OpenFileFolderAction.ID : OpenFileAction.ID),
when: CONTEXT_DEBUGGER_INTERESTED_IN_ACTIVE_EDITOR.toNegated()
});
let debugKeybindingLabel = '';
viewsRegistry.registerViewWelcomeContent(StartView.ID, {
viewsRegistry.registerViewWelcomeContent(WelcomeView.ID, {
content: localize('runAndDebugAction', "[Run and Debug{0}](command:{1})", debugKeybindingLabel, StartAction.ID),
preconditions: [CONTEXT_DEBUGGER_INTERESTED_IN_ACTIVE_EDITOR]
});
viewsRegistry.registerViewWelcomeContent(StartView.ID, {
viewsRegistry.registerViewWelcomeContent(WelcomeView.ID, {
content: localize('customizeRunAndDebug', "To customize Run and Debug [create a launch.json file](command:{0}).", ConfigureAction.ID),
when: WorkbenchStateContext.notEqualsTo('empty')
});
viewsRegistry.registerViewWelcomeContent(StartView.ID, {
viewsRegistry.registerViewWelcomeContent(WelcomeView.ID, {
content: localize('customizeRunAndDebugOpenFolder', "To customize Run and Debug, [open a folder](command:{0}) and create a launch.json file.", isMacintosh ? OpenFileFolderAction.ID : OpenFolderAction.ID),
when: WorkbenchStateContext.isEqualTo('empty')
});

View file

@ -33,7 +33,7 @@ import { IOutputService } from 'vs/workbench/contrib/output/common/output';
import { StartStopProblemCollector, WatchingProblemCollector, ProblemCollectorEventKind, ProblemHandlingStrategy } from 'vs/workbench/contrib/tasks/common/problemCollectors';
import {
Task, CustomTask, ContributedTask, RevealKind, CommandOptions, ShellConfiguration, RuntimeType, PanelKind,
TaskEvent, TaskEventKind, ShellQuotingOptions, ShellQuoting, CommandString, CommandConfiguration, ExtensionTaskSource, TaskScope, RevealProblemKind, DependsOrder
TaskEvent, TaskEventKind, ShellQuotingOptions, ShellQuoting, CommandString, CommandConfiguration, ExtensionTaskSource, TaskScope, RevealProblemKind, DependsOrder, TaskSourceKind
} from 'vs/workbench/contrib/tasks/common/tasks';
import {
ITaskSystem, ITaskSummary, ITaskExecuteResult, TaskExecuteKind, TaskError, TaskErrors, ITaskResolver,
@ -545,7 +545,7 @@ export class TerminalTaskSystem implements ITaskSystem {
resolveSet.process.path = envPath;
}
}
resolvedVariables = taskSystemInfo.resolveVariables(workspaceFolder, resolveSet).then(async (resolved) => {
resolvedVariables = taskSystemInfo.resolveVariables(workspaceFolder, resolveSet, TaskSourceKind.toConfigurationTarget(task._source.kind)).then(async (resolved) => {
this.mergeMaps(alreadyResolved, resolved.variables);
resolved.variables = new Map(alreadyResolved);
if (isProcess) {
@ -563,7 +563,7 @@ export class TerminalTaskSystem implements ITaskSystem {
unresolved.forEach(variable => variablesArray.push(variable));
return new Promise((resolve, reject) => {
this.configurationResolverService.resolveWithInteraction(workspaceFolder, variablesArray, 'tasks').then(async (resolvedVariablesMap: Map<string, string> | undefined) => {
this.configurationResolverService.resolveWithInteraction(workspaceFolder, variablesArray, 'tasks', undefined, TaskSourceKind.toConfigurationTarget(task._source.kind)).then(async (resolvedVariablesMap: Map<string, string> | undefined) => {
if (resolvedVariablesMap) {
this.mergeMaps(alreadyResolved, resolvedVariablesMap);
resolvedVariablesMap = new Map(alreadyResolved);

View file

@ -10,6 +10,7 @@ import { Event } from 'vs/base/common/event';
import { Platform } from 'vs/base/common/platform';
import { IWorkspaceFolder } from 'vs/platform/workspace/common/workspace';
import { Task, TaskEvent, KeyedTaskIdentifier } from './tasks';
import { ConfigurationTarget } from 'vs/platform/configuration/common/configuration';
export const enum TaskErrors {
NotConfigured,
@ -118,7 +119,7 @@ export interface TaskSystemInfo {
platform: Platform;
context: any;
uriProvider: (this: void, path: string) => URI;
resolveVariables(workspaceFolder: IWorkspaceFolder, toResolve: ResolveSet): Promise<ResolvedVariables>;
resolveVariables(workspaceFolder: IWorkspaceFolder, toResolve: ResolveSet, target: ConfigurationTarget): Promise<ResolvedVariables>;
getDefaultShellAndArgs(): Promise<{ shell: string, args: string[] | string | undefined }>;
}

View file

@ -15,6 +15,7 @@ import { IWorkspaceFolder, IWorkspace } from 'vs/platform/workspace/common/works
import { RawContextKey } from 'vs/platform/contextkey/common/contextkey';
import { TaskDefinitionRegistry } from 'vs/workbench/contrib/tasks/common/taskDefinitionRegistry';
import { IExtensionDescription } from 'vs/platform/extensions/common/extensions';
import { ConfigurationTarget } from 'vs/platform/configuration/common/configuration';
export const TASK_RUNNING_STATE = new RawContextKey<boolean>('taskRunning', false);
@ -377,6 +378,14 @@ export namespace TaskSourceKind {
export const InMemory: 'inMemory' = 'inMemory';
export const WorkspaceFile: 'workspaceFile' = 'workspaceFile';
export const User: 'user' = 'user';
export function toConfigurationTarget(kind: string): ConfigurationTarget {
switch (kind) {
case TaskSourceKind.User: return ConfigurationTarget.USER;
case TaskSourceKind.WorkspaceFile: return ConfigurationTarget.WORKSPACE;
default: return ConfigurationTarget.WORKSPACE_FOLDER;
}
}
}
export interface TaskSourceConfigElement {

View file

@ -22,7 +22,7 @@ export type ViewsWelcomeExtensionPoint = ViewWelcome[];
export const ViewIdentifierMap: { [key: string]: string } = {
'explorer': 'workbench.explorer.emptyView',
'debug': 'workbench.debug.startView',
'debug': 'workbench.debug.welcome',
'scm': 'workbench.scm',
};
@ -40,7 +40,6 @@ const viewsWelcomeExtensionPointSchema = Object.freeze<IConfigurationPropertySch
[ViewsWelcomeExtensionPointFields.view]: {
type: 'string',
description: nls.localize('contributes.viewsWelcome.view.view', "Target view identifier for this welcome content."),
enum: Object.keys(ViewIdentifierMap),
},
[ViewsWelcomeExtensionPointFields.contents]: {
type: 'string',

View file

@ -11,7 +11,7 @@ import { Schemas } from 'vs/base/common/network';
import { toResource } from 'vs/workbench/common/editor';
import { IStringDictionary, forEach, fromMap } from 'vs/base/common/collections';
import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { IConfigurationService, ConfigurationTarget } from 'vs/platform/configuration/common/configuration';
import { ICommandService } from 'vs/platform/commands/common/commands';
import { IWorkspaceFolder, IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace';
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
@ -86,12 +86,12 @@ export abstract class BaseConfigurationResolverService extends AbstractVariableR
}, envVariables);
}
public async resolveWithInteractionReplace(folder: IWorkspaceFolder | undefined, config: any, section?: string, variables?: IStringDictionary<string>): Promise<any> {
public async resolveWithInteractionReplace(folder: IWorkspaceFolder | undefined, config: any, section?: string, variables?: IStringDictionary<string>, target?: ConfigurationTarget): Promise<any> {
// resolve any non-interactive variables and any contributed variables
config = this.resolveAny(folder, config);
// resolve input variables in the order in which they are encountered
return this.resolveWithInteraction(folder, config, section, variables).then(mapping => {
return this.resolveWithInteraction(folder, config, section, variables, target).then(mapping => {
// finally substitute evaluated command variables (if there are any)
if (!mapping) {
return null;
@ -103,14 +103,14 @@ export abstract class BaseConfigurationResolverService extends AbstractVariableR
});
}
public async resolveWithInteraction(folder: IWorkspaceFolder | undefined, config: any, section?: string, variables?: IStringDictionary<string>): Promise<Map<string, string> | undefined> {
public async resolveWithInteraction(folder: IWorkspaceFolder | undefined, config: any, section?: string, variables?: IStringDictionary<string>, target?: ConfigurationTarget): Promise<Map<string, string> | undefined> {
// resolve any non-interactive variables and any contributed variables
const resolved = await this.resolveAnyMap(folder, config);
config = resolved.newConfig;
const allVariableMapping: Map<string, string> = resolved.resolvedVariables;
// resolve input and command variables in the order in which they are encountered
return this.resolveWithInputAndCommands(folder, config, variables, section).then(inputOrCommandMapping => {
return this.resolveWithInputAndCommands(folder, config, variables, section, target).then(inputOrCommandMapping => {
if (this.updateMapping(inputOrCommandMapping, allVariableMapping)) {
return allVariableMapping;
}
@ -139,7 +139,7 @@ export abstract class BaseConfigurationResolverService extends AbstractVariableR
*
* @param variableToCommandMap Aliases for commands
*/
private async resolveWithInputAndCommands(folder: IWorkspaceFolder | undefined, configuration: any, variableToCommandMap?: IStringDictionary<string>, section?: string): Promise<IStringDictionary<string> | undefined> {
private async resolveWithInputAndCommands(folder: IWorkspaceFolder | undefined, configuration: any, variableToCommandMap?: IStringDictionary<string>, section?: string, target?: ConfigurationTarget): Promise<IStringDictionary<string> | undefined> {
if (!configuration) {
return Promise.resolve(undefined);
@ -148,9 +148,18 @@ export abstract class BaseConfigurationResolverService extends AbstractVariableR
// get all "inputs"
let inputs: ConfiguredInput[] = [];
if (folder && this.workspaceContextService.getWorkbenchState() !== WorkbenchState.EMPTY && section) {
let result = this.configurationService.getValue<any>(section, { resource: folder.uri });
if (result) {
inputs = result.inputs;
let result = this.configurationService.inspect(section, { resource: folder.uri });
if (result && (result.userValue || result.workspaceValue || result.workspaceFolderValue)) {
switch (target) {
case ConfigurationTarget.USER: inputs = (<any>result.userValue)?.inputs; break;
case ConfigurationTarget.WORKSPACE: inputs = (<any>result.workspaceValue)?.inputs; break;
default: inputs = (<any>result.workspaceFolderValue)?.inputs;
}
} else {
const valueResult = this.configurationService.getValue<any>(section, { resource: folder.uri });
if (valueResult) {
inputs = valueResult.inputs;
}
}
}

View file

@ -6,6 +6,7 @@
import { IStringDictionary } from 'vs/base/common/collections';
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
import { IWorkspaceFolder } from 'vs/platform/workspace/common/workspace';
import { ConfigurationTarget } from 'vs/platform/configuration/common/configuration';
export const IConfigurationResolverService = createDecorator<IConfigurationResolverService>('configurationResolverService');
@ -29,13 +30,13 @@ export interface IConfigurationResolverService {
* @param section For example, 'tasks' or 'debug'. Used for resolving inputs.
* @param variables Aliases for commands.
*/
resolveWithInteractionReplace(folder: IWorkspaceFolder | undefined, config: any, section?: string, variables?: IStringDictionary<string>): Promise<any>;
resolveWithInteractionReplace(folder: IWorkspaceFolder | undefined, config: any, section?: string, variables?: IStringDictionary<string>, target?: ConfigurationTarget): Promise<any>;
/**
* Similar to resolveWithInteractionReplace, except without the replace. Returns a map of variables and their resolution.
* Keys in the map will be of the format input:variableName or command:variableName.
*/
resolveWithInteraction(folder: IWorkspaceFolder | undefined, config: any, section?: string, variables?: IStringDictionary<string>): Promise<Map<string, string> | undefined>;
resolveWithInteraction(folder: IWorkspaceFolder | undefined, config: any, section?: string, variables?: IStringDictionary<string>, target?: ConfigurationTarget): Promise<Map<string, string> | undefined>;
/**
* Contributes a variable that can be resolved later. Consumers that use resolveAny, resolveWithInteraction,

View file

@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/
import { URI } from 'vs/base/common/uri';
import { IFileSystemProviderWithFileReadWriteCapability, FileSystemProviderCapabilities, IFileChange, IWatchOptions, IStat, FileOverwriteOptions, FileType, FileDeleteOptions, FileWriteOptions, FileChangeType, FileSystemProviderErrorCode } from 'vs/platform/files/common/files';
import { IFileSystemProviderWithFileReadWriteCapability, FileSystemProviderCapabilities, IFileChange, IWatchOptions, IStat, FileOverwriteOptions, FileType, FileDeleteOptions, FileWriteOptions, FileChangeType } from 'vs/platform/files/common/files';
import { Disposable, IDisposable } from 'vs/base/common/lifecycle';
import { Event, Emitter } from 'vs/base/common/event';
import { VSBuffer } from 'vs/base/common/buffer';
@ -53,13 +53,13 @@ export abstract class KeyValueLogProvider extends Disposable implements IFileSys
size: 0
};
}
return Promise.reject(new FileSystemError(resource, FileSystemProviderErrorCode.FileNotFound));
return Promise.reject(FileSystemError.FileNotFound(resource));
}
async readdir(resource: URI): Promise<[string, FileType][]> {
const hasKey = await this.hasKey(resource.path);
if (hasKey) {
return Promise.reject(new FileSystemError(resource, FileSystemProviderErrorCode.FileNotADirectory));
return Promise.reject(FileSystemError.FileNotADirectory(resource));
}
const keys = await this.getAllKeys();
const files: Map<string, [string, FileType]> = new Map<string, [string, FileType]>();
@ -79,7 +79,7 @@ export abstract class KeyValueLogProvider extends Disposable implements IFileSys
async readFile(resource: URI): Promise<Uint8Array> {
const hasKey = await this.hasKey(resource.path);
if (!hasKey) {
return Promise.reject(new FileSystemError(resource, FileSystemProviderErrorCode.FileNotFound));
return Promise.reject(FileSystemError.FileNotFound(resource));
}
const value = await this.getValue(resource.path);
return VSBuffer.fromString(value).buffer;
@ -90,7 +90,7 @@ export abstract class KeyValueLogProvider extends Disposable implements IFileSys
if (!hasKey) {
const files = await this.readdir(resource);
if (files.length) {
return Promise.reject(new FileSystemError(resource, FileSystemProviderErrorCode.FileIsADirectory));
return Promise.reject(FileSystemError.FileIsADirectory(resource));
}
}
await this.setValue(resource.path, VSBuffer.wrap(content).toString());

View file

@ -62,10 +62,11 @@ import { ITunnelService } from 'vs/platform/remote/common/tunnel';
import { TunnelService } from 'vs/workbench/services/remote/common/tunnelService';
import { ILoggerService } from 'vs/platform/log/common/log';
import { FileLoggerService } from 'vs/platform/log/common/fileLogService';
import { IUserDataSyncStoreService, IUserDataSyncService, IUserDataSyncLogService, ISettingsSyncService, IUserDataAutoSyncService } from 'vs/platform/userDataSync/common/userDataSync';
import { IUserDataSyncStoreService, IUserDataSyncService, IUserDataSyncLogService, ISettingsSyncService, IUserDataAutoSyncService, IUserDataSyncBackupStoreService } from 'vs/platform/userDataSync/common/userDataSync';
import { AuthenticationService, IAuthenticationService } from 'vs/workbench/services/authentication/browser/authenticationService';
import { UserDataSyncLogService } from 'vs/platform/userDataSync/common/userDataSyncLog';
import { UserDataSyncStoreService } from 'vs/platform/userDataSync/common/userDataSyncStoreService';
import { UserDataSyncBackupStoreService } from 'vs/platform/userDataSync/common/userDataSyncBackupStoreService';
import { UserDataSyncService } from 'vs/platform/userDataSync/common/userDataSyncService';
import { SettingsSynchroniser } from 'vs/platform/userDataSync/common/settingsSync';
import { IAuthenticationTokenService, AuthenticationTokenService } from 'vs/platform/authentication/common/authentication';
@ -83,6 +84,7 @@ registerSingleton(ILoggerService, FileLoggerService);
registerSingleton(IAuthenticationService, AuthenticationService);
registerSingleton(IUserDataSyncLogService, UserDataSyncLogService);
registerSingleton(IUserDataSyncStoreService, UserDataSyncStoreService);
registerSingleton(IUserDataSyncBackupStoreService, UserDataSyncBackupStoreService);
registerSingleton(IAuthenticationTokenService, AuthenticationTokenService);
registerSingleton(IUserDataAutoSyncService, UserDataAutoSyncService);
registerSingleton(ISettingsSyncService, SettingsSynchroniser);