Skip to content

Commit

Permalink
Several features: package contents, basic support for function groups…
Browse files Browse the repository at this point in the history
…, programs, classes
  • Loading branch information
marcellourbani committed Oct 24, 2018
2 parents cae5381 + 7a4799b commit 6497d4c
Show file tree
Hide file tree
Showing 17 changed files with 362 additions and 169 deletions.
6 changes: 5 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
# ABAP remote filesystem for visual studio code

Ideally one day this will allow you to edit your ABAP code directly in Visual studio code
Very early stages, for now it only displays a list of packages
Very early stages, for now it only displays some packages and a handful of object types, no local objects,subroutines in object lists...

Even things that do work need a big refactor
![image](https://user-images.githubusercontent.com/2453277/47466602-dd99dc00-d7e9-11e8-97ed-28e23dfd8f90.png)
syntax highlighting added manually with the [ABAP language extension](https://marketplace.visualstudio.com/items?itemName=larshp.vscode-abap),picture was too lame without it :)

## Features

Expand Down
21 changes: 21 additions & 0 deletions src/abap/AbapFunctionGroup.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { AbapObject } from "./AbapObject"
import { Uri } from "vscode"

export class AbapFunctionGroup extends AbapObject {
isLeaf() {
return false
}
getUri(base: Uri): Uri {
const ptype = encodeURIComponent(this.type)
const pname = encodeURIComponent(this.name)
const techname = encodeURIComponent(
this.namespace() === ""
? "SAPL" + this.name
: `/${this.namespace()}/SAPL${this.nameinns}`
)
return base.with({
path: "/sap/bc/adt/repository/nodestructure",
query: `parent_name=${pname}&parent_tech_name=${techname}&parent_type=${ptype}&withShortDescriptions=true`
})
}
}
8 changes: 8 additions & 0 deletions src/abap/AbapFunctionModule.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { AbapObject } from "./AbapObject"
import { Uri } from "vscode"

export class AbapFunctionModule extends AbapObject {
getUri(base: Uri): Uri {
return base.with({ path: this.path + "/source/main" })
}
}
35 changes: 35 additions & 0 deletions src/abap/AbapObject.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { Uri } from "vscode"

export interface AbapObjectPart {
type: string
name: string
parent: AbapObject
}

export class AbapObject {
type: string
name: string
path: string

constructor(type: string, name: string, path: string) {
this.name = name
this.type = type
this.path = path
}

isLeaf() {
return true
}

getUri(base: Uri): Uri {
return base.with({ path: this.path + "/source/main" })
}
namespace(): string {
return this.name.match(/^\//)
? this.name.replace(/^\/([^\/]*)\/.*/, "$1")
: ""
}
nameinns(): string {
return this.name.replace(/^\/[^\/]*\/(.*)/, "$1")
}
}
23 changes: 23 additions & 0 deletions src/abap/AbapObjectFactory.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { ObjectNode } from "../adt/AdtParser"
import { AbapObject } from "./AbapObject"
import { AbapPackage } from "./AbapPackage"
import { AbapFunctionGroup } from "./AbapFunctionGroup"
import { AbapSimpleObject } from "./AbapSimpleObject"

export function fromObjectNode(node: ObjectNode): AbapObject {
let objtype = AbapObject
switch (node.OBJECT_TYPE) {
case "DEVC/K":
objtype = AbapPackage
break
case "FUGR/F":
objtype = AbapFunctionGroup
break
case "TABL/DT":
case "DOMA/DT":
case "DTEL/DE":
objtype = AbapSimpleObject
break
}
return new objtype(node.OBJECT_TYPE, node.OBJECT_NAME, node.OBJECT_URI)
}
16 changes: 16 additions & 0 deletions src/abap/AbapPackage.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
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)
return base.with({
path: "/sap/bc/adt/repository/nodestructure",
query: `parent_name=${pname}&parent_tech_name=${pname}&parent_type=${ptype}&withShortDescriptions=true`
})
}
}
8 changes: 8 additions & 0 deletions src/abap/AbapSimpleObject.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { AbapObject } from "./AbapObject"
import { Uri } from "vscode"

export class AbapSimpleObject extends AbapObject {
getUri(base: Uri): Uri {
return base.with({ path: this.path })
}
}
36 changes: 9 additions & 27 deletions src/abapFsProvider.ts
Original file line number Diff line number Diff line change
@@ -1,56 +1,38 @@
import * as vscode from "vscode"
import { AdtPathManager } from "./adt/AdtPathManager"
import { AdtNode } from "./adt/AdtNode"

export class AbapFsProvider implements vscode.FileSystemProvider {
private _pathManager = new AdtPathManager()
private _eventEmitter = new vscode.EventEmitter<vscode.FileChangeEvent[]>()
readonly onDidChangeFile: vscode.Event<vscode.FileChangeEvent[]> = this
._eventEmitter.event
rooturl: string = ""
root: AdtNode = new AdtNode("")
watch(
uri: vscode.Uri,
options: { recursive: boolean; excludes: string[] }
): vscode.Disposable {
throw new Error("Method not implemented.")
}
stat(uri: vscode.Uri): vscode.FileStat | Thenable<vscode.FileStat> {
const uristring = uri.toString()
if (this.rooturl === "") this.rooturl = uristring
if (this.rooturl === uristring) {
const newroot = this._pathManager
.fetchDirectory(uristring)
.then(newroot => (this.root = newroot))
return newroot
}
throw new Error("not found")
return this._pathManager.fetchFileOrDir(uri)
}
readDirectory(
uri: vscode.Uri
): [string, vscode.FileType][] | Thenable<[string, vscode.FileType][]> {
if (uri.toString() !== this.rooturl || !this.root) {
throw new Error("Only root directory for now...")
}
const result: [string, vscode.FileType][] = []
Array.from(this.root.entries).forEach(([key, value]) =>
result.push([key, value.type])
)
const dir = this._pathManager.getDirectory(uri)
if (dir)
Array.from(dir.entries).forEach(([key, value]) =>
result.push([key.replace(/\//g, "_"), value.type])
)
return result
}
createDirectory(uri: vscode.Uri): void | Thenable<void> {
throw new Error("Method not implemented.")
}
readFile(uri: vscode.Uri): Uint8Array | Thenable<Uint8Array> {
// 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,
Expand Down
20 changes: 10 additions & 10 deletions src/adt/AdtConnection.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
import * as request from "request"
// import { AdtPathClassifier } from "./AdtPathClassifier"
import { Uri } from "vscode"

enum ConnStatus {
new,
active,
Expand All @@ -19,6 +22,7 @@ export class AdtConnection {
this.username = username
this.password = password
}

isActive(): boolean {
return this._status === ConnStatus.active
}
Expand All @@ -42,14 +46,9 @@ export class AdtConnection {
})
}

request(
path: string,
method: string = "GET",
config: request.Options | Object = {}
): Promise<request.Response> {
let relativePath = path.replace(/(?:adt:\/)?\/[^\/]*\/sap\/bc\/adt/i, "")
const request = this.createrequest(relativePath, method, config)
return this.myrequest(request)
request(uri: Uri, method: string): Promise<request.Response> {
const path = uri.query ? uri.path + "?" + uri.query : uri.path
return this.myrequest(this.createrequest(path, method))
}

private createrequest(
Expand All @@ -67,9 +66,10 @@ export class AdtConnection {
},
method,
headers: {
"x-csrf-token": this._csrftoken
"x-csrf-token": this._csrftoken,
Accept: "*/*"
}
}
} as request.Options //workaround for compiler bug
}
private myrequest(options: request.Options): Promise<request.Response> {
return new Promise((resolve, reject) => {
Expand Down
19 changes: 0 additions & 19 deletions src/adt/AdtFile.ts

This file was deleted.

55 changes: 24 additions & 31 deletions src/adt/AdtNode.ts
Original file line number Diff line number Diff line change
@@ -1,40 +1,33 @@
import { FileStat, FileType } from "vscode"
import { AdtFile } from "./AdtFile"
import { ObjectNode } from "./AdtParser"

export type AdtDirItem = AdtFile | AdtNode
import { FileStat, FileType, Uri } from "vscode"

export class AdtNode implements FileStat {
static fromTreeContent(fromTreeContent: ObjectNode[]): AdtNode {
const node = new AdtNode("")
return node
}
type: FileType = FileType.Directory
name: string
type: FileType
ctime: number
mtime: number
size: number = 0
entries: Map<string, AdtDirItem>
constructor(
name: string,
ctime: number = Date.now(),
mtime: number = Date.now()
) {
this.name = name
this.ctime = ctime
this.mtime = mtime
entries: Map<string, AdtNode>
uri: Uri
fetched: boolean
body: Buffer | undefined
needRefresh(): any {
return !this.fetched
}
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
}
setChildrenFromTreeContent(children: ObjectNode[]): AdtNode {
this.entries.clear()
children.forEach(objnode => {
this.entries.set(
objnode.OBJECT_NAME,
objnode.EXPANDABLE
? new AdtNode(objnode.OBJECT_NAME)
: new AdtFile(objnode.OBJECT_NAME)
)
})
return this
setContents(body: string): void {
this.body = Buffer.from(body)
this.size = this.body.length
this.fetched = true
}
}
4 changes: 2 additions & 2 deletions src/adt/AdtParser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,12 +52,12 @@ export const getNode = (...args: any[]) => {
)
return [functions, rest]
}
const fn = (...fargs: any) => {
const fn = (...fargs: any[]) => {
const [functions, rest] = split(...fargs)
if (functions.length === 0) return rest[0]
const piped = pipe(...functions)
return rest.length === 0
? (...iargs: any) => fn(piped, ...iargs)
? (...iargs: any[]) => fn(piped, ...iargs)
: piped(...rest)
}
return fn(...args)
Expand Down
Loading

0 comments on commit 6497d4c

Please sign in to comment.