diff --git a/src/adt/AbapObject.ts b/src/abap/AbapObject.ts similarity index 94% rename from src/adt/AbapObject.ts rename to src/abap/AbapObject.ts index 964dec7..ce41c51 100644 --- a/src/adt/AbapObject.ts +++ b/src/abap/AbapObject.ts @@ -17,6 +17,10 @@ export class AbapObject { this.path = path } + isLeaf() { + return true + } + getUri(base: Uri): Uri { return base.with({ path: this.path }) } diff --git a/src/adt/AbapObjectFactory.ts b/src/abap/AbapObjectFactory.ts similarity index 89% rename from src/adt/AbapObjectFactory.ts rename to src/abap/AbapObjectFactory.ts index 6414dcc..16efc58 100644 --- a/src/adt/AbapObjectFactory.ts +++ b/src/abap/AbapObjectFactory.ts @@ -1,4 +1,4 @@ -import { ObjectNode } from "./AdtParser" +import { ObjectNode } from "../adt/AdtParser" import { AbapObject } from "./AbapObject" import { AbapPackage } from "./AbapPackage" diff --git a/src/adt/AbapPackage.ts b/src/abap/AbapPackage.ts similarity index 91% rename from src/adt/AbapPackage.ts rename to src/abap/AbapPackage.ts index 6571419..a7deded 100644 --- a/src/adt/AbapPackage.ts +++ b/src/abap/AbapPackage.ts @@ -2,6 +2,9 @@ import { AbapObject } from "./AbapObject" import { Uri } from "vscode" export class AbapPackage extends AbapObject { + isLeaf() { + return false + } getUri(base: Uri): Uri { const ptype = encodeURIComponent(this.type) const pname = encodeURIComponent(this.name) diff --git a/src/abapFsProvider.ts b/src/abapFsProvider.ts index 9ccdd23..57252e2 100644 --- a/src/abapFsProvider.ts +++ b/src/abapFsProvider.ts @@ -30,15 +30,9 @@ export class AbapFsProvider implements vscode.FileSystemProvider { throw new Error("Method not implemented.") } readFile(uri: vscode.Uri): Uint8Array | Thenable { - // if (uri.path === "/dummy.abap" && this.root) { - // return this.root.then(x => { - // const child = x.entries.get("dummy.abap") - // if (child && child instanceof AdtFile && child.data) { - // return child.data - // } - // }) - // } - throw new Error("Method not implemented.") + const file = this._pathManager.find(uri) + if (file && file.body) return file.body + return new Uint8Array([]) } writeFile( uri: vscode.Uri, diff --git a/src/adt/AdtNode.ts b/src/adt/AdtNode.ts index e88a110..2d694b3 100644 --- a/src/adt/AdtNode.ts +++ b/src/adt/AdtNode.ts @@ -1,23 +1,32 @@ import { FileStat, FileType, Uri } from "vscode" export class AdtNode implements FileStat { - type: FileType = FileType.Directory + type: FileType ctime: number mtime: number size: number = 0 entries: Map uri: Uri + fetched: boolean + body: Buffer | undefined needRefresh(): any { - return this.type === FileType.Directory && this.entries.size === 0 + return !this.fetched } - constructor(path: Uri) { + constructor(path: Uri, isDirectory: boolean, fetched: boolean) { this.ctime = Date.now() this.mtime = Date.now() this.entries = new Map() this.uri = path + this.type = isDirectory ? FileType.Directory : FileType.File + this.fetched = fetched } childPath(childname: string): string { const sep = this.uri.path.match(/\/$/) || childname.match(/^\//) ? "" : "/" return this.uri.path + sep + childname } + setContents(body: Buffer): void { + this.body = body + this.size = body.length + this.fetched = true + } } diff --git a/src/adt/AdtPathManager.ts b/src/adt/AdtPathManager.ts index 2e96ffa..0b7b49e 100644 --- a/src/adt/AdtPathManager.ts +++ b/src/adt/AdtPathManager.ts @@ -2,39 +2,64 @@ import { AdtNode } from "./AdtNode" import { Response } from "request" import { getNodeStructureTreeContent, ObjectNode } from "./AdtParser" import { getServer, AdtServer } from "./AdtServer" -import { fromObjectNode } from "./AbapObjectFactory" -import { Uri } from "vscode" - -const asPromise = (x: AdtNode) => new Promise(resolve => resolve(x)) +import { fromObjectNode } from "../abap/AbapObjectFactory" +import { Uri, FileSystemError, FileType } from "vscode" export class AdtPathManager { getDirectory(uri: Uri): AdtNode | undefined { return getServer(uri.authority).getDirectory(uri.path) } + find(uri: Uri): AdtNode | undefined { + const server = getServer(uri.authority) + let node = server.getDirectory(uri.path) + if (node) return node + const matches = uri.path.match(/(.*)\/([^\/]+)$/) + if (matches) { + const [dir, name] = matches.slice(1) + let parent = server.getDirectory(dir) + let node = parent && parent.entries.get(name) + if (node) return node + } + } - parse(uri: Uri, response: Response, server: AdtServer): Promise { - return getNodeStructureTreeContent(response.body).then( - (children: ObjectNode[]) => { - const node = new AdtNode(uri) - server.addNodes(node, children.map(fromObjectNode)) - - return node - } + parse( + uri: Uri, + response: Response, + server: AdtServer, + node: AdtNode | undefined + ): Promise | AdtNode { + if ( + response.request.uri.path && + response.request.uri.path.match(/\/nodestructure/i) ) + return getNodeStructureTreeContent(response.body).then( + (children: ObjectNode[]) => { + if (node) node.entries.clear() + else node = new AdtNode(uri, true, true) + server.addNodes(node, children.map(fromObjectNode)) + node.fetched = true + return node + } + ) + else if (node && node.type === FileType.File) { + node.setContents(response.body) + return node + } + throw FileSystemError.FileNotFound(uri.path) } - fetchFileOrDir(vsUrl: Uri): Promise { + fetchFileOrDir(vsUrl: Uri): Promise | AdtNode { const server = getServer(vsUrl.authority) - const cached = server.getDirectory(vsUrl.path) + const cached = this.find(vsUrl) if (cached && !cached.needRefresh()) { - return asPromise(cached) + return cached } const url = server.actualUri(vsUrl) return server.connectionP .then(conn => conn.request(url, "POST")) - .then(response => this.parse(vsUrl, response, server)) + .then(response => this.parse(vsUrl, response, server, cached)) } } diff --git a/src/adt/AdtServer.ts b/src/adt/AdtServer.ts index b66f9c0..455cc32 100644 --- a/src/adt/AdtServer.ts +++ b/src/adt/AdtServer.ts @@ -1,8 +1,8 @@ import { AdtConnectionManager } from "./AdtConnectionManager" import { AdtConnection } from "./AdtConnection" import { AdtNode } from "./AdtNode" -import { Uri, FileSystemError } from "vscode" -import { AbapObject } from "./AbapObject" +import { Uri, FileSystemError, FileType } from "vscode" +import { AbapObject } from "../abap/AbapObject" // visual studio paths are hierarchic, adt ones aren't // so we need a way to translate the hierarchic ones to the original ones // this file is concerned with telling whether a path is a real ADT one or one from vscode @@ -28,9 +28,14 @@ export class AdtServer { private addChildrenToNs(node: AdtNode, objects: AbapObject[]) { objects.forEach(object => { const childname = node.childPath(object.nameinns()) - const child = new AdtNode(node.uri.with({ path: childname })) + const child = new AdtNode( + node.uri.with({ path: childname }), + !object.isLeaf(), + false + ) node.entries.set(object.nameinns(), child) this.objectUris.set(childname, object.getUri(node.uri)) + if(child.type=== FileType.Directory)this.directories.set(childname,child) }) } @@ -52,16 +57,27 @@ export class AdtServer { return map }, new Map()) + //for every namespace create a node, add the children to it + // so package /foo/bar will be rendered in + // a namespace folder foo + // with a package bar inside namespaces.forEach((objects, name) => { - if (name === "") this.addChildrenToNs(parent, objects) - else { + if (name !== "") { const nodeName = parent.childPath(name) - const node = new AdtNode(parent.uri.with({ path: nodeName })) + const node = new AdtNode( + parent.uri.with({ path: nodeName }), + true, + true + ) parent.entries.set(name, node) this.addChildrenToNs(node, objects) this.directories.set(nodeName, node) } }) + //add objects without a namespace + namespaces.forEach((objects, name) => { + if (name === "") this.addChildrenToNs(parent, objects) + }) } getDirectory(name: string): AdtNode | undefined {