diff --git a/rascal-lsp/src/main/java/org/rascalmpl/vscode/lsp/uri/FallbackResolver.java b/rascal-lsp/src/main/java/org/rascalmpl/vscode/lsp/uri/FallbackResolver.java index 894109d4a..d68da5976 100644 --- a/rascal-lsp/src/main/java/org/rascalmpl/vscode/lsp/uri/FallbackResolver.java +++ b/rascal-lsp/src/main/java/org/rascalmpl/vscode/lsp/uri/FallbackResolver.java @@ -57,14 +57,11 @@ import org.rascalmpl.vscode.lsp.uri.jsonrpc.VSCodeUriResolverClient; import org.rascalmpl.vscode.lsp.uri.jsonrpc.VSCodeUriResolverServer; import org.rascalmpl.vscode.lsp.uri.jsonrpc.VSCodeVFS; -import org.rascalmpl.vscode.lsp.uri.jsonrpc.messages.IOResult; import org.rascalmpl.vscode.lsp.uri.jsonrpc.messages.ISourceLocationRequest; import org.rascalmpl.vscode.lsp.uri.jsonrpc.messages.WriteFileRequest; import org.rascalmpl.vscode.lsp.util.Lazy; - import com.github.benmanes.caffeine.cache.Cache; import com.github.benmanes.caffeine.cache.Caffeine; - import io.usethesource.vallang.ISourceLocation; public class FallbackResolver implements ISourceLocationInputOutput, ISourceLocationWatcher, ILogicalSourceLocationResolver { @@ -100,14 +97,9 @@ private static VSCodeUriResolverClient getClient() throws IOException { return result; } - private static T call(Function> target) throws IOException { + private static T call(Function> target) throws IOException { try { - var waitingForServer = target.apply(getServer()); - var result = waitingForServer.get(5, TimeUnit.MINUTES); - if (result.getErrorCode() != 0) { - throw new IOException("" + result.getErrorCode() + ": " + result.getErrorMessage()); - } - return result; + return target.apply(getServer()).get(5, TimeUnit.MINUTES); } catch (TimeoutException te) { throw new IOException("VSCode took too long to reply, interruption to avoid deadlocks"); diff --git a/rascal-lsp/src/main/java/org/rascalmpl/vscode/lsp/uri/jsonrpc/VSCodeUriResolverServer.java b/rascal-lsp/src/main/java/org/rascalmpl/vscode/lsp/uri/jsonrpc/VSCodeUriResolverServer.java index 9dd1da004..722d68259 100644 --- a/rascal-lsp/src/main/java/org/rascalmpl/vscode/lsp/uri/jsonrpc/VSCodeUriResolverServer.java +++ b/rascal-lsp/src/main/java/org/rascalmpl/vscode/lsp/uri/jsonrpc/VSCodeUriResolverServer.java @@ -32,7 +32,6 @@ import org.rascalmpl.vscode.lsp.uri.jsonrpc.messages.BooleanResult; import org.rascalmpl.vscode.lsp.uri.jsonrpc.messages.DirectoryListingResult; import org.rascalmpl.vscode.lsp.uri.jsonrpc.messages.FileAttributesResult; -import org.rascalmpl.vscode.lsp.uri.jsonrpc.messages.IOResult; import org.rascalmpl.vscode.lsp.uri.jsonrpc.messages.ISourceLocationRequest; import org.rascalmpl.vscode.lsp.uri.jsonrpc.messages.NumberResult; import org.rascalmpl.vscode.lsp.uri.jsonrpc.messages.ReadFileResult; @@ -99,32 +98,32 @@ default CompletableFuture isWritable(ISourceLocationRequest req) @JsonRequest("rascal/vfs/output/writeFile") - default CompletableFuture writeFile(WriteFileRequest req) { + default CompletableFuture writeFile(WriteFileRequest req) { throw new UnsupportedOperationException(); } @JsonRequest("rascal/vfs/output/mkDirectory") - default CompletableFuture mkDirectory(ISourceLocationRequest req) { + default CompletableFuture mkDirectory(ISourceLocationRequest req) { throw new UnsupportedOperationException(); } @JsonRequest("rascal/vfs/output/remove") - default CompletableFuture remove(ISourceLocationRequest req) { + default CompletableFuture remove(ISourceLocationRequest req) { throw new UnsupportedOperationException(); } @JsonRequest("rascal/vfs/output/rename") - default CompletableFuture rename(RenameRequest req) { + default CompletableFuture rename(RenameRequest req) { throw new UnsupportedOperationException(); } @JsonRequest("rascal/vfs/watcher/watch") - default CompletableFuture watch(WatchRequest req) { + default CompletableFuture watch(WatchRequest req) { throw new UnsupportedOperationException(); } @JsonRequest("rascal/vfs/watcher/unwatch") - default CompletableFuture unwatch(WatchRequest req) { + default CompletableFuture unwatch(WatchRequest req) { throw new UnsupportedOperationException(); } diff --git a/rascal-lsp/src/main/java/org/rascalmpl/vscode/lsp/uri/jsonrpc/messages/BooleanResult.java b/rascal-lsp/src/main/java/org/rascalmpl/vscode/lsp/uri/jsonrpc/messages/BooleanResult.java index 2c3eb095a..0d15433f8 100644 --- a/rascal-lsp/src/main/java/org/rascalmpl/vscode/lsp/uri/jsonrpc/messages/BooleanResult.java +++ b/rascal-lsp/src/main/java/org/rascalmpl/vscode/lsp/uri/jsonrpc/messages/BooleanResult.java @@ -30,17 +30,16 @@ import org.checkerframework.checker.nullness.qual.Nullable; import org.eclipse.lsp4j.jsonrpc.validation.NonNull; -public class BooleanResult extends IOResult { - private @Nullable Boolean result; +public class BooleanResult { + private Boolean result; - public BooleanResult(@NonNull int errorCode, @Nullable String errorMessage, @Nullable Boolean result) { - super(errorCode, errorMessage); + public BooleanResult(@NonNull int errorCode, String errorMessage, Boolean result) { this.result = result; } public BooleanResult() {} - public @Nullable Boolean getResult() { + public Boolean getResult() { return result; } diff --git a/rascal-lsp/src/main/java/org/rascalmpl/vscode/lsp/uri/jsonrpc/messages/DirectoryListingResult.java b/rascal-lsp/src/main/java/org/rascalmpl/vscode/lsp/uri/jsonrpc/messages/DirectoryListingResult.java index 42e38bb04..da971d5fb 100644 --- a/rascal-lsp/src/main/java/org/rascalmpl/vscode/lsp/uri/jsonrpc/messages/DirectoryListingResult.java +++ b/rascal-lsp/src/main/java/org/rascalmpl/vscode/lsp/uri/jsonrpc/messages/DirectoryListingResult.java @@ -30,24 +30,23 @@ import java.util.Objects; import org.checkerframework.checker.nullness.qual.Nullable; -public class DirectoryListingResult extends IOResult { +public class DirectoryListingResult { - private String @Nullable[] entries; - private boolean @Nullable[] areDirectory; + private String[] entries; + private boolean[] areDirectory; - public DirectoryListingResult(int errorCode, @Nullable String errorMessage, String @Nullable[] entries, boolean @Nullable[] areDirectory) { - super(errorCode, errorMessage); + public DirectoryListingResult(String [] entries, boolean [] areDirectory) { this.entries = entries; this.areDirectory = areDirectory; } public DirectoryListingResult() {} - public String @Nullable[] getEntries() { + public String [] getEntries() { return entries; } - public boolean @Nullable[] getAreDirectory() { + public boolean [] getAreDirectory() { return areDirectory; } diff --git a/rascal-lsp/src/main/java/org/rascalmpl/vscode/lsp/uri/jsonrpc/messages/FileAttributesResult.java b/rascal-lsp/src/main/java/org/rascalmpl/vscode/lsp/uri/jsonrpc/messages/FileAttributesResult.java index 4567a7e24..5a0955c2b 100644 --- a/rascal-lsp/src/main/java/org/rascalmpl/vscode/lsp/uri/jsonrpc/messages/FileAttributesResult.java +++ b/rascal-lsp/src/main/java/org/rascalmpl/vscode/lsp/uri/jsonrpc/messages/FileAttributesResult.java @@ -29,19 +29,17 @@ import java.util.Objects; import org.checkerframework.checker.nullness.qual.Nullable; -import org.eclipse.lsp4j.jsonrpc.validation.NonNull; import org.rascalmpl.uri.FileAttributes; -public class FileAttributesResult extends IOResult { - private @Nullable Boolean exists; - private @Nullable Integer type; - private @Nullable Long ctime; - private @Nullable Long mtime; - private @Nullable Integer size; - private @Nullable Integer permissions; +public class FileAttributesResult { + private Boolean exists; + private Integer type; + private Long ctime; + private Long mtime; + private Integer size; + private Integer permissions; - public FileAttributesResult(@NonNull int errorCode, @Nullable String errorMessage, @Nullable Boolean exists, @Nullable Integer type, @Nullable Long ctime, @Nullable Long mtime, @Nullable Integer size, @Nullable Integer permissions) { - super(errorCode, errorMessage); + public FileAttributesResult(Boolean exists, Integer type, Long ctime, Long mtime, Integer size, Integer permissions) { this.exists = exists; this.type = type; this.ctime = ctime; diff --git a/rascal-lsp/src/main/java/org/rascalmpl/vscode/lsp/uri/jsonrpc/messages/IOResult.java b/rascal-lsp/src/main/java/org/rascalmpl/vscode/lsp/uri/jsonrpc/messages/IOResult.java deleted file mode 100644 index aef00dbfd..000000000 --- a/rascal-lsp/src/main/java/org/rascalmpl/vscode/lsp/uri/jsonrpc/messages/IOResult.java +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright (c) 2018-2025, NWO-I CWI and Swat.engineering - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ -package org.rascalmpl.vscode.lsp.uri.jsonrpc.messages; - -import java.util.Objects; -import org.checkerframework.checker.nullness.qual.Nullable; -import org.eclipse.lsp4j.jsonrpc.validation.NonNull; - -public class IOResult { - @NonNull - private int errorCode; - - @Nullable - private String errorMessage; - - public IOResult() { - } - - public IOResult(@NonNull int errorCode, @Nullable String errorMessage) { - this.errorCode = errorCode; - this.errorMessage = errorMessage; - } - - public int getErrorCode() { - return errorCode; - } - - public @Nullable String getErrorMessage() { - return errorMessage; - } - - @Override - public boolean equals(@Nullable Object obj) { - if (obj instanceof IOResult) { - var other = (IOResult)obj; - return errorCode == other.errorCode - && Objects.equals(errorMessage, other.errorMessage); - } - return false; - } - - @Override - public int hashCode() { - return errorCode - + 7 * (errorMessage == null ? 4 : errorMessage.hashCode()); - } - - @Override - public String toString() { - return "IOResult [errorCode=" + errorCode + ", errorMessage=" + errorMessage + "]"; - } - -} diff --git a/rascal-lsp/src/main/java/org/rascalmpl/vscode/lsp/uri/jsonrpc/messages/NumberResult.java b/rascal-lsp/src/main/java/org/rascalmpl/vscode/lsp/uri/jsonrpc/messages/NumberResult.java index 0e4e7a104..bfe35921a 100644 --- a/rascal-lsp/src/main/java/org/rascalmpl/vscode/lsp/uri/jsonrpc/messages/NumberResult.java +++ b/rascal-lsp/src/main/java/org/rascalmpl/vscode/lsp/uri/jsonrpc/messages/NumberResult.java @@ -28,19 +28,17 @@ import java.util.Objects; import org.checkerframework.checker.nullness.qual.Nullable; -import org.eclipse.lsp4j.jsonrpc.validation.NonNull; -public class NumberResult extends IOResult { - private @Nullable Integer result; +public class NumberResult { + private Integer result; - public NumberResult(@NonNull int errorCode, @Nullable String errorMessage, @Nullable Integer result) { - super(errorCode, errorMessage); + public NumberResult(Integer result) { this.result = result; } public NumberResult() {} - public @Nullable Integer getResult() { + public Integer getResult() { return result; } diff --git a/rascal-lsp/src/main/java/org/rascalmpl/vscode/lsp/uri/jsonrpc/messages/ReadFileResult.java b/rascal-lsp/src/main/java/org/rascalmpl/vscode/lsp/uri/jsonrpc/messages/ReadFileResult.java index d433f7fd5..ee421d895 100644 --- a/rascal-lsp/src/main/java/org/rascalmpl/vscode/lsp/uri/jsonrpc/messages/ReadFileResult.java +++ b/rascal-lsp/src/main/java/org/rascalmpl/vscode/lsp/uri/jsonrpc/messages/ReadFileResult.java @@ -30,18 +30,17 @@ import org.checkerframework.checker.nullness.qual.Nullable; import org.eclipse.lsp4j.jsonrpc.validation.NonNull; -public class ReadFileResult extends IOResult { +public class ReadFileResult { - private @Nullable String contents; + private String contents; - public ReadFileResult(@NonNull int errorCode, @Nullable String errorMessage, @Nullable String contents) { - super(errorCode, errorMessage); + public ReadFileResult(String contents) { this.contents = contents; } public ReadFileResult() {} - public @Nullable String getContents() { + public String getContents() { return contents; } diff --git a/rascal-lsp/src/main/java/org/rascalmpl/vscode/lsp/uri/jsonrpc/messages/TimestampResult.java b/rascal-lsp/src/main/java/org/rascalmpl/vscode/lsp/uri/jsonrpc/messages/TimestampResult.java index 8b24c8d3c..2f42b6086 100644 --- a/rascal-lsp/src/main/java/org/rascalmpl/vscode/lsp/uri/jsonrpc/messages/TimestampResult.java +++ b/rascal-lsp/src/main/java/org/rascalmpl/vscode/lsp/uri/jsonrpc/messages/TimestampResult.java @@ -29,17 +29,16 @@ import java.util.Objects; import org.checkerframework.checker.nullness.qual.Nullable; -public class TimestampResult extends IOResult { - private @Nullable Long timestamp; +public class TimestampResult { + private Long timestamp; - public TimestampResult(int errorCode, @Nullable String errorMessage, @Nullable Long timestamp) { - super(errorCode, errorMessage); + public TimestampResult(Long timestamp) { this.timestamp = timestamp; } public TimestampResult() {} - public @Nullable Long getTimestamp() { + public Long getTimestamp() { return timestamp; } diff --git a/rascal-vscode-extension/src/RascalTerminalLinkProvider.ts b/rascal-vscode-extension/src/RascalTerminalLinkProvider.ts index d1bab07a0..16f87cb09 100644 --- a/rascal-vscode-extension/src/RascalTerminalLinkProvider.ts +++ b/rascal-vscode-extension/src/RascalTerminalLinkProvider.ts @@ -92,7 +92,7 @@ export class RascalTerminalLinkProvider implements TerminalLinkProvider; - mkDirectory(req: ISourceLocationRequest): Promise; - remove(req: ISourceLocationRequest): Promise; - rename(req: RenameRequest): Promise; + writeFile(req: WriteFileRequest ): Promise; + mkDirectory(req: ISourceLocationRequest): Promise; + remove(req: ISourceLocationRequest): Promise; + rename(req: RenameRequest): Promise; } function connectOutputHandler(connection: rpc.MessageConnection, handler: ISourceLocationOutput) { - function req (method: string, h: rpc.RequestHandler1) { + function req (method: string, h: rpc.RequestHandler1) { connection.onRequest( - new rpc.RequestType1("rascal/vfs/output/" + method), + new rpc.RequestType1("rascal/vfs/output/" + method), h.bind(handler)); } - req("writeFile", handler.writeFile); - req("mkDirectory", handler.mkDirectory); - req("remove", handler.remove); - req("rename", handler.rename); + req("writeFile", handler.writeFile); + req("mkDirectory", handler.mkDirectory); + req("remove", handler.remove); + req("rename", handler.rename); } // Rascal's interface reduce to a subset we can support interface ISourceLocationWatcher { - watch(newWatch: WatchRequest): Promise; - unwatch(removeWatch: WatchRequest): Promise; + watch(newWatch: WatchRequest): Promise; + unwatch(removeWatch: WatchRequest): Promise; } function connectWatchHandler(connection: rpc.MessageConnection, handler: ISourceLocationWatcher) { - function req (method: string, h: rpc.RequestHandler1) { + function req (method: string, h: rpc.RequestHandler1) { connection.onRequest( - new rpc.RequestType1("rascal/vfs/watcher/" + method), + new rpc.RequestType1("rascal/vfs/watcher/" + method), h.bind(handler)); } - req("watch", handler.watch); - req("unwatch", handler.unwatch); + req("watch", handler.watch); + req("unwatch", handler.unwatch); } // client side implementation receiving watch events @@ -133,49 +133,41 @@ interface ISourceLocationRequest { uri: ISourceLocation; } -interface IOResult { - /** - * Error type occurred (or 0 if success) - */ - errorCode: integer; - errorMessage?: string; -} - -interface ReadFileResult extends IOResult { +interface ReadFileResult { /** * base64 encoding of file */ - contents?: string; + contents: string; } -export interface BooleanResult extends IOResult { - result?: boolean; +export interface BooleanResult { + result: boolean; } -export interface TimestampResult extends IOResult { +export interface TimestampResult { /** * Epoch seconds */ - timestamp?: number; + timestamp: number; } -export interface DirectoryListingResult extends IOResult { - entries?: string[]; - areDirectory?: boolean[] +export interface DirectoryListingResult { + entries: string[]; + areDirectory: boolean[] } -export interface NumberResult extends IOResult { - result?: number; +export interface NumberResult { + result: number; } -export interface FileAttributesResult extends IOResult { - exists? : boolean; - type?: vscode.FileType; - ctime?: number; - mtime?: number; - size?: number; - permissions?: vscode.FilePermission; +export interface FileAttributesResult { + exists : boolean; + type: vscode.FileType; + ctime: number; + mtime: number; + size: number; + permissions: vscode.FilePermission; } export interface WriteFileRequest extends ISourceLocationRequest { @@ -212,6 +204,13 @@ export interface ISourceLocationChanged { changeType: ISourceLocationChangeType; } +enum ErrorCodes { + generic = -1, + fileSystem = -2, + nativeRascal = -3 + +} + export class VSCodeUriResolverServer implements Disposable { private readonly server: Server; @@ -269,52 +268,34 @@ export class VSCodeUriResolverServer implements Disposable { } -function toUri(req: ISourceLocationRequest | ISourceLocation): vscode.Uri { - if (typeof req !== 'string') { - req = req.uri; - } - return vscode.Uri.parse(req); -} -async function asyncCatcher(build: () => Promise): Promise { +async function asyncCatcher(build: () => Thenable): Promise { try { return await build(); } catch (e: unknown) { - return { - errorCode: 1, - errorMessage: "" + e - }; + if (e instanceof vscode.FileSystemError) { + throw new rpc.ResponseError(ErrorCodes.fileSystem, e.message, e.code); + } + if (e instanceof rpc.ResponseError) { + throw e; + } + throw new rpc.ResponseError(ErrorCodes.generic, "" + e); } } -async function asyncVoidCatcher(run: (() => Promise) | Thenable): Promise { - try { +async function asyncVoidCatcher(run: (() => Promise) | Thenable): Promise { + return asyncCatcher(() => { if (typeof run === "function") { - await run(); + return run(); } else { - await run; + return run; } - return { - errorCode: 0 - }; - } - catch (e: unknown) { - return { - errorCode: 1, - errorMessage: "" + e - }; - } + }); } -async function buildIOError(message: string, errorCode = -1): Promise { - return { - errorCode: errorCode, - errorMessage: message - }; -} class ResolverClient implements VSCodeResolverServer, Disposable { private readonly connection: rpc.MessageConnection; private readonly watchListener: WatchEventReceiver; @@ -338,68 +319,64 @@ class ResolverClient implements VSCodeResolverServer, Disposable { connectWatchHandler(connection, this); } + toUri(req: ISourceLocationRequest | ISourceLocation): vscode.Uri { + if (typeof req !== 'string') { + req = req.uri; + } + const uri = vscode.Uri.parse(req); + if (this.isRascalNative(uri)) { + throw new rpc.ResponseError(ErrorCodes.nativeRascal, "Cannot request VFS jobs on native rascal URIs: " + req); + } + return uri; + } + async readFile(req: ISourceLocationRequest): Promise { - if (this.isRascalNative(req)) { - return buildIOError("Cannot read a from a rascal uri: " + req.uri); - } return asyncCatcher(async () => { errorCode: 0, contents: Buffer.from( - await this.fs.readFile(toUri(req)) + await this.fs.readFile(this.toUri(req)) ).toString("base64") }); } - isRascalNative(req: ISourceLocationRequest) : boolean { - const scheme = req.uri.substring(0, req.uri.indexOf(":")); + isRascalNative(req: ISourceLocationRequest | vscode.Uri) : boolean { + //this.rascalNativeSchemes.has(uri.scheme) + const scheme = "scheme" in req ? req.scheme : req.uri.substring(0, req.uri.indexOf(":")); return this.rascalNativeSchemes.has(scheme); } async exists(req: ISourceLocationRequest): Promise { - if (this.isRascalNative(req)) { - return buildIOError("Cannot exist on a rascal uri: " + req.uri); - } try { await this.stat(req); - return { - errorCode: 0, - result: true - }; + return { result: true }; } catch (_e) { - return { - errorCode: 0, - result: false - }; + return { result: false }; } } async fileStat(req: ISourceLocationRequest): Promise { - const fileInfo = await this.stat(req); - return { - errorCode: 0, - exists: (await this.exists(req)).result!, - type: fileInfo.type.valueOf(), - ctime: fileInfo.ctime, - mtime: fileInfo.mtime, - size: fileInfo.size, - permissions: fileInfo.permissions ? fileInfo.permissions.valueOf() : 0 - }; + return asyncCatcher(async () => { + const fileInfo = await this.stat(req); + return { + exists: true, + type: fileInfo.type.valueOf(), + ctime: fileInfo.ctime, + mtime: fileInfo.mtime, + size: fileInfo.size, + permissions: fileInfo.permissions ? fileInfo.permissions.valueOf() : 0 + }; + }); } private async stat(req: ISourceLocationRequest): Promise { - const uri = toUri(req); - if (this.rascalNativeSchemes.has(uri.scheme)) { - throw new Error("Cannot stat a URI that's actually on the rascal side: " + req.uri); - } - return this.fs.stat(toUri(req)); + return this.fs.stat(this.toUri(req)); } private async timeStampResult(req: ISourceLocationRequest, mapper: (s :vscode.FileStat) => number): Promise { return asyncCatcher(async () => { - errorCode: 0, timestamp: mapper((await this.stat(req))) }); } @@ -413,7 +390,6 @@ class ResolverClient implements VSCodeResolverServer, Disposable { private async numberResult(req: ISourceLocationRequest, mapper: (s: vscode.FileStat) => number): Promise { return asyncCatcher(async () => { - errorCode: 0, result: mapper((await this.stat(req))) }); } @@ -424,7 +400,6 @@ class ResolverClient implements VSCodeResolverServer, Disposable { private async boolResult(req: ISourceLocationRequest, mapper: (s :vscode.FileStat) => boolean): Promise { return asyncCatcher(async () => { - errorCode: 0, result: mapper((await this.stat(req))) }); } @@ -443,30 +418,23 @@ class ResolverClient implements VSCodeResolverServer, Disposable { return this.boolResult(req, _ => true); } - isWritable(req: ISourceLocationRequest): Promise { - const scheme = toUri(req).scheme; + async isWritable(req: ISourceLocationRequest): Promise { + const scheme = this.toUri(req).scheme; const writable = this.fs.isWritableFileSystem(scheme); if (writable === undefined) { - return buildIOError("Unsupported scheme: " + scheme, 1); + throw new rpc.ResponseError(ErrorCodes.fileSystem, "Unsupported scheme: " + scheme, "Unsupported file system"); } if (!writable) { // not a writable file system, so no need to check the uri - return asyncCatcher(async () => { - errorCode: 0, - result: false - }); + return {result : false }; } return this.boolResult(req, f => f.permissions === undefined || (f.permissions & vscode.FilePermission.Readonly) === 0); } async list(req: ISourceLocationRequest): Promise { - if (this.isRascalNative(req)) { - return buildIOError("Cannot list on a rascal uri: " + req.uri); - } return asyncCatcher(async () => { - const entries = await this.fs.readDirectory(toUri(req)); + const entries = await this.fs.readDirectory(this.toUri(req)); return { - errorCode: 0, entries: entries.map(([entry, _type], _index) => entry), areDirectory: entries.map(([_entry, type], _index) => type === vscode.FileType.Directory) }; @@ -475,60 +443,39 @@ class ResolverClient implements VSCodeResolverServer, Disposable { - async writeFile(req: WriteFileRequest): Promise { - if (this.isRascalNative(req)) { - return buildIOError("Cannot writeFile on a rascal uri: " + req.uri); - } + async writeFile(req: WriteFileRequest): Promise { return asyncVoidCatcher( - this.fs.writeFile(toUri(req), Buffer.from(req.content, "base64")) + this.fs.writeFile(this.toUri(req), Buffer.from(req.content, "base64")) ); } - async mkDirectory(req: ISourceLocationRequest): Promise { - if (this.isRascalNative(req)) { - return buildIOError("Cannot mkDirectory on a rascal uri: " + req.uri); - } - return asyncVoidCatcher(this.fs.createDirectory(toUri(req))); + async mkDirectory(req: ISourceLocationRequest): Promise { + return asyncVoidCatcher(this.fs.createDirectory(this.toUri(req))); } - async remove(req: ISourceLocationRequest): Promise { - if (this.isRascalNative(req)) { - return buildIOError("Cannot remove on a rascal uri: " + req.uri); - } - return asyncVoidCatcher(this.fs.delete(toUri(req))); + async remove(req: ISourceLocationRequest): Promise { + return asyncVoidCatcher(this.fs.delete(this.toUri(req))); } - rename(req: RenameRequest): Promise { - const from = toUri(req.from); - const to = toUri(req.to); - if (this.rascalNativeSchemes.has(from.scheme) || this.rascalNativeSchemes.has(to.scheme)) { - return buildIOError("Cannot rename on a rascal uri: " + req.from + " and " + req.to); - } + async rename(req: RenameRequest): Promise { + const from = this.toUri(req.from); + const to = this.toUri(req.to); return asyncVoidCatcher(this.fs.rename(from, to, { overwrite: req.overwrite })); } - private activeWatches = new Map(); + private readonly activeWatches = new Map(); - watch(newWatch: WatchRequest): Promise { - if (this.isRascalNative(newWatch)) { - return buildIOError("Cannot watch on a rascal uri: " + newWatch.uri); - } + async watch(newWatch: WatchRequest): Promise { const watchKey = newWatch.uri + newWatch.recursive; if (!this.activeWatches.has(watchKey)) { - const watcher = new WatcherCallbacks(newWatch.uri, newWatch.recursive, this.watchListener, newWatch.watcher); + const watcher = new WatcherCallbacks(this.toUri(newWatch.uri), newWatch.recursive, this.watchListener, newWatch.watcher); this.activeWatches.set(watchKey, watcher); this.toClear.push(watcher); - return Promise.resolve({ errorCode: 0 }); + return; } - return Promise.resolve({ - errorCode: 1, - errorMessage: 'Watch already defined for: ' + newWatch.uri - }); + throw new rpc.ResponseError(ErrorCodes.fileSystem, 'Watch already defined for: ' + newWatch.uri, 'AlreadyDefined'); } - unwatch(removeWatch: WatchRequest): Promise { - if (this.isRascalNative(removeWatch)) { - return buildIOError("Cannot watch on a rascal uri: " + removeWatch.uri); - } + async unwatch(removeWatch: WatchRequest): Promise { const watchKey = removeWatch.uri + removeWatch.recursive; const watcher = this.activeWatches.get(watchKey); if (watcher) { @@ -538,12 +485,9 @@ class ResolverClient implements VSCodeResolverServer, Disposable { if (index >= 0) { this.toClear.splice(index, 1); } - return Promise.resolve({ errorCode: 0 }); + return; } - return Promise.resolve({ - errorCode: 1, - errorMessage: 'Watch not defined for: ' + removeWatch.uri - }); + throw new rpc.ResponseError(ErrorCodes.fileSystem, 'Watch not defined for: ' + removeWatch.uri, 'NotDefined'); } dispose() { @@ -563,11 +507,11 @@ class WatcherCallbacks implements Disposable { private readonly watchId: string; private readonly toClear: Disposable[] = []; private readonly watchListener: WatchEventReceiver; - constructor(uri: string, recursive: boolean, watchListener: WatchEventReceiver, watchId: string) { + constructor(uri: vscode.Uri, recursive: boolean, watchListener: WatchEventReceiver, watchId: string) { this.watchId = watchId; this.watchListener = watchListener; const newWatcher = vscode.workspace.createFileSystemWatcher( - new vscode.RelativePattern(toUri(uri), recursive ? '**/*' : '*') + new vscode.RelativePattern(uri, recursive ? '**/*' : '*') ); this.toClear.push(newWatcher); newWatcher.onDidCreate(e => this.sendWatchEvent(e, ISourceLocationChangeType.created), this.toClear);