Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix user storage regressions #8313

Merged
merged 3 commits into from
Aug 6, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 7 additions & 4 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,16 @@
- [output] `OutputWidget#setInput` has been removed. The _Output_ view automatically shows the channel when calling `OutputChannel#show`. Moved the `OutputCommands` namespace from the `output-contribution` to its dedicated `output-commands` module to overcome a DI cycle. [#8243](https://github.com/eclipse-theia/theia/pull/8243)
- [example app] updated yarn.lock so that the latest version of `vscode-ripgrep` is used (`v1.8.0`). This way we can benefit from the recently added support for it using proxy settings when fetching the platform-specific `ripgrep` executable, after npm package install. This should make it a lot easier to build our example application in corporate settings, behind a firewall. [#8280](https://github.com/eclipse-theia/theia/pull/8280)
- Note to downstream IDE designers: this change will not have an effect beyond this repo's example application. If it's desirable for your product to have the latest `vscode-ripgrep`, you should do similarly in your own `yarn.lock`.
<a name="1_4_0_deprecate_file_sytem"></a>
- [[filesystem]](#1_4_0_deprecate_file_sytem) `FileSystem` and `FileSystemWatcher` services are deprecated [#7908](https://github.com/eclipse-theia/theia/pull/7908)
<a name="1.5.0_deprecate_file_sytem"></a>
- [[filesystem]](#1.5.0_deprecate_file_sytem) `FileSystem` and `FileSystemWatcher` services are deprecated [#7908](https://github.com/eclipse-theia/theia/pull/7908)
- On the backend there is no anymore `FileSystem` implementation. One has to use Node.js APIs instead.
- On the frontend `FileService` should be used instead. It was ported from VS Code for compatibility with VS Code extensions.
- On the frontend `EnvVariableServer` should be used instead to access the current user home and available drives.
<a name="1_4_0_usestorage_as_fs_provider"></a>
- [[userstorage]](#1_4_0_usestorage_as_fs_provider) `UserStorageService` was replaced by the user data fs provider [#7908](https://github.com/eclipse-theia/theia/pull/7908)
<a name="1.5.0_usestorage_as_fs_provider"></a>
- [[userstorage]](#1.5.0_usestorage_as_fs_provider) `UserStorageService` was replaced by the user data fs provider [#7908](https://github.com/eclipse-theia/theia/pull/7908)
<a name="1.5.0_root_user_storage_uri"></a>
- [[user-storage]](#1.5.0_root_user_storage_uri) settings URI must start with `/user` root to satisfy expectations of `FileService` []()
- If you implement a custom user storage make sure to check old relative locations, otherwise it can cause user data loss.

## v1.4.0

Expand Down
4 changes: 4 additions & 0 deletions packages/core/src/browser/keybinding.ts
Original file line number Diff line number Diff line change
Expand Up @@ -615,6 +615,10 @@ export class KeybindingRegistry {
this.keymaps[i] = [];
}
}

getKeybindingsByScope(scope: KeybindingScope): ScopedKeybinding[] {
return this.keymaps[scope];
}
}

export namespace KeybindingRegistry {
Expand Down
7 changes: 6 additions & 1 deletion packages/core/src/browser/preferences/preference-provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -159,12 +159,17 @@ export abstract class PreferenceProvider implements Disposable {
}

/**
* undefined if cannot be provided for the given resource uri
* Returns undefined if there is no valid config URI for the given resource URI.
*/
getConfigUri(resourceUri?: string): URI | undefined {
return undefined;
}

/**
* Returns undefined if there is config URI at all for the given resource URI.
Copy link
Member Author

@akosyakov akosyakov Aug 5, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

there is no

*/
getContainingConfigUri?(resourceUri?: string): URI | undefined;

static merge(source: JSONValue | undefined, target: JSONValue): JSONValue {
if (source === undefined || !JSONExt.isObject(source)) {
return JSONExt.deepCopy(target);
Expand Down
14 changes: 14 additions & 0 deletions packages/core/src/browser/preferences/preference-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ export interface PreferenceService extends Disposable {
overriddenPreferenceName(preferenceName: string): OverridePreferenceName | undefined;

resolve<T>(preferenceName: string, defaultValue?: T, resourceUri?: string): PreferenceResolveResult<T>;
getConfigUri(scope: PreferenceScope, resourceUri?: string): URI | undefined;
}

/**
Expand Down Expand Up @@ -358,4 +359,17 @@ export class PreferenceServiceImpl implements PreferenceService {
value: result.value !== undefined ? deepFreeze(result.value) : defaultValue
};
}

getConfigUri(scope: PreferenceScope, resourceUri?: string): URI | undefined {
const provider = this.getProvider(scope);
if (!provider) {
return undefined;
}
const configUri = provider.getConfigUri(resourceUri);
if (configUri) {
return configUri;
}
return provider.getContainingConfigUri && provider.getContainingConfigUri(resourceUri);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import { Emitter, Event } from '../../../common';
import { OverridePreferenceName } from '../preference-contribution';
import URI from '../../../common/uri';
import { PreferenceChanges } from '../preference-service';
import { PreferenceScope } from '../preference-scope';

@injectable()
export class MockPreferenceService implements PreferenceService {
Expand Down Expand Up @@ -60,4 +61,5 @@ export class MockPreferenceService implements PreferenceService {

// eslint-disable-next-line @typescript-eslint/no-explicit-any
validate(name: string, value: any): boolean { return true; }
getConfigUri(scope: PreferenceScope, resourceUri?: string): URI | undefined { return undefined; }
}
2 changes: 1 addition & 1 deletion packages/filesystem/src/browser/file-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1662,7 +1662,7 @@ export class FileService {
* Converts to an underlying fs provider resource format.
*
* For example converting `user-storage` resources to `file` resources under a user home:
* user-storage:/settings.json => file://home/.theia/settings.json
* user-storage:/user/settings.json => file://home/.theia/settings.json
*/
async toUnderlyingResource(resource: URI): Promise<URI> {
let provider = await this.withProvider(resource);
Expand Down
49 changes: 28 additions & 21 deletions packages/filesystem/src/common/delegating-file-system-provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ export class DelegatingFileSystemProvider implements Required<FileSystemProvider
protected readonly toDispose = new DisposableCollection()
) {
this.toDispose.push(this.onDidChangeFileEmitter);
this.toDispose.push(delegate.onDidChangeFile(changes => this.handleFileChanges(changes)));
}

dispose(): void {
Expand All @@ -52,70 +53,70 @@ export class DelegatingFileSystemProvider implements Required<FileSystemProvider
}

watch(resource: URI, opts: WatchOptions): Disposable {
return this.delegate.watch(this.options.uriConverter.to(resource), opts);
return this.delegate.watch(this.toUnderlyingResource(resource), opts);
}

stat(resource: URI): Promise<Stat> {
return this.delegate.stat(this.options.uriConverter.to(resource));
return this.delegate.stat(this.toUnderlyingResource(resource));
}

access(resource: URI, mode?: number): Promise<void> {
if (hasAccessCapability(this.delegate)) {
return this.delegate.access(this.options.uriConverter.to(resource), mode);
return this.delegate.access(this.toUnderlyingResource(resource), mode);
}
throw new Error('not supported');
}

fsPath(resource: URI): Promise<string> {
if (hasAccessCapability(this.delegate)) {
return this.delegate.fsPath(this.options.uriConverter.to(resource));
return this.delegate.fsPath(this.toUnderlyingResource(resource));
}
throw new Error('not supported');
}

mkdir(resource: URI): Promise<void> {
return this.delegate.mkdir(this.options.uriConverter.to(resource));
return this.delegate.mkdir(this.toUnderlyingResource(resource));
}

rename(from: URI, to: URI, opts: FileOverwriteOptions): Promise<void> {
return this.delegate.rename(this.options.uriConverter.to(from), this.options.uriConverter.to(to), opts);
return this.delegate.rename(this.toUnderlyingResource(from), this.toUnderlyingResource(to), opts);
}

copy(from: URI, to: URI, opts: FileOverwriteOptions): Promise<void> {
if (hasFileFolderCopyCapability(this.delegate)) {
return this.delegate.copy(this.options.uriConverter.to(from), this.options.uriConverter.to(to), opts);
return this.delegate.copy(this.toUnderlyingResource(from), this.toUnderlyingResource(to), opts);
}
throw new Error('not supported');
}

readFile(resource: URI): Promise<Uint8Array> {
if (hasReadWriteCapability(this.delegate)) {
return this.delegate.readFile(this.options.uriConverter.to(resource));
return this.delegate.readFile(this.toUnderlyingResource(resource));
}
throw new Error('not supported');
}

readFileStream(resource: URI, opts: FileReadStreamOptions, token: CancellationToken): ReadableStreamEvents<Uint8Array> {
if (hasFileReadStreamCapability(this.delegate)) {
return this.delegate.readFileStream(this.options.uriConverter.to(resource), opts, token);
return this.delegate.readFileStream(this.toUnderlyingResource(resource), opts, token);
}
throw new Error('not supported');
}

readdir(resource: URI): Promise<[string, FileType][]> {
return this.delegate.readdir(this.options.uriConverter.to(resource));
return this.delegate.readdir(this.toUnderlyingResource(resource));
}

writeFile(resource: URI, content: Uint8Array, opts: FileWriteOptions): Promise<void> {
if (hasReadWriteCapability(this.delegate)) {
return this.delegate.writeFile(this.options.uriConverter.to(resource), content, opts);
return this.delegate.writeFile(this.toUnderlyingResource(resource), content, opts);
}
throw new Error('not supported');
}

open(resource: URI, opts: FileOpenOptions): Promise<number> {
if (hasOpenReadWriteCloseCapability(this.delegate)) {
return this.delegate.open(this.options.uriConverter.to(resource), opts);
return this.delegate.open(this.toUnderlyingResource(resource), opts);
}
throw new Error('not supported');
}
Expand All @@ -142,7 +143,7 @@ export class DelegatingFileSystemProvider implements Required<FileSystemProvider
}

delete(resource: URI, opts: FileDeleteOptions): Promise<void> {
return this.delegate.delete(this.options.uriConverter.to(resource), opts);
return this.delegate.delete(this.toUnderlyingResource(resource), opts);
}

updateFile(resource: URI, changes: TextDocumentContentChangeEvent[], opts: FileUpdateOptions): Promise<FileUpdateResult> {
Expand All @@ -155,7 +156,7 @@ export class DelegatingFileSystemProvider implements Required<FileSystemProvider
protected handleFileChanges(changes: readonly FileChange[]): void {
const delegatingChanges: FileChange[] = [];
for (const change of changes) {
const delegatingResource = this.options.uriConverter.from(change.resource);
const delegatingResource = this.fromUnderlyingResource(change.resource);
if (delegatingResource) {
delegatingChanges.push({
resource: delegatingResource,
Expand All @@ -172,21 +173,25 @@ export class DelegatingFileSystemProvider implements Required<FileSystemProvider
* Converts to an underlying fs provider resource format.
*
* For example converting `user-storage` resources to `file` resources under a user home:
* user-storage:/settings.json => file://home/.theia/settings.json
* user-storage:/user/settings.json => file://home/.theia/settings.json
*/
toUnderlyingResource(resource: URI): URI {
return this.options.uriConverter.to(resource);
const underlying = this.options.uriConverter.to(resource);
if (!underlying) {
throw new Error('invalid resource: ' + resource.toString());
}
return underlying;
}

/**
* Converts from an underlying fs provider resource format.
*
* For example converting `file` resources unser a user home to `user-storage` resource:
* - file://home/.theia/settings.json => user-storage:/settings.json
* - file://home/.theia/settings.json => user-storage:/user/settings.json
* - file://documents/some-document.txt => undefined
*/
fromUnderlyingResource(resource: URI): URI {
return this.options.uriConverter.to(resource);
fromUnderlyingResource(resource: URI): URI | undefined {
return this.options.uriConverter.from(resource);
}

}
Expand All @@ -197,11 +202,13 @@ export namespace DelegatingFileSystemProvider {
export interface URIConverter {
/**
* Converts to an underlying fs provider resource format.
* Returns undefined if the given resource is not valid resource.
*
* For example converting `user-storage` resources to `file` resources under a user home:
* user-storage:/settings.json => file://home/.theia/settings.json
* user-storage:/user/settings.json => file://home/.theia/settings.json
* user-storage:/settings.json => undefined
*/
to(resource: URI): URI;
to(resource: URI): URI | undefined;
/**
* Converts from an underlying fs provider resource format.
*
Expand Down
Loading