Skip to content

Commit 8d062fc

Browse files
Merge pull request #384 from gjsjohnmurray/isfs-folder-settings-experiment
Support server-side (isfs) folder settings, snippets, debug configs etc
2 parents 596f951 + 11601be commit 8d062fc

File tree

4 files changed

+104
-25
lines changed

4 files changed

+104
-25
lines changed

src/api/index.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -500,4 +500,12 @@ export class AtelierAPI {
500500
};
501501
return this.request(1, "GET", `%SYS/jobs`, null, params);
502502
}
503+
504+
// v1+
505+
public getCSPApps(detail = false): Promise<Atelier.Response> {
506+
const params = {
507+
detail: detail ? 1 : 0,
508+
};
509+
return this.request(1, "GET", `%SYS/cspapps/${this.ns || ""}`, null, params);
510+
}
503511
}

src/commands/compile.ts

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -251,18 +251,22 @@ export async function importAndCompile(askFlags = false, document?: vscode.TextD
251251
// console.error(error);
252252
throw error;
253253
})
254-
.then(() => compile([file], flags));
254+
.then(() => {
255+
if (!file.fileName.startsWith("\\.vscode\\")) {
256+
compile([file], flags);
257+
}
258+
});
255259
}
256260

257261
// Compiles all files types in the namespace
258-
export async function namespaceCompile(askFLags = false): Promise<any> {
262+
export async function namespaceCompile(askFlags = false): Promise<any> {
259263
const api = new AtelierAPI();
260264
const fileTypes = ["*.CLS", "*.MAC", "*.INC", "*.BAS"];
261265
if (!config("conn").active) {
262266
throw new Error(`No Active Connection`);
263267
}
264268
const defaultFlags = config().compileFlags;
265-
const flags = askFLags ? await compileFlags() : defaultFlags;
269+
const flags = askFlags ? await compileFlags() : defaultFlags;
266270
if (flags === undefined) {
267271
// User cancelled
268272
return;

src/providers/FileSystemPovider/FileSystemProvider.ts

Lines changed: 59 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { Directory } from "./Directory";
66
import { File } from "./File";
77
import { fireOtherStudioAction, OtherStudioAction } from "../../commands/studio";
88
import { StudioOpenDialog } from "../../queries";
9+
import { redirectDotvscodeRoot } from "../../utils/index";
910

1011
declare function setTimeout(callback: (...args: any[]) => void, ms: number, ...args: any[]): NodeJS.Timeout;
1112

@@ -61,29 +62,63 @@ export class FileSystemProvider implements vscode.FileSystemProvider {
6162
} else {
6263
filter = "*.cls,*.inc,*.mac,*.int";
6364
}
64-
const folder = csp ? (uri.path.endsWith("/") ? uri.path : uri.path + "/") : uri.path.replace(/\//g, ".");
65+
const folder = !csp
66+
? uri.path.replace(/\//g, ".")
67+
: uri.path === "/"
68+
? ""
69+
: uri.path.endsWith("/")
70+
? uri.path
71+
: uri.path + "/";
6572
const spec = csp ? folder + filter : folder.length > 1 ? folder.slice(1) + "/" + filter : filter;
6673
const dir = "1";
6774
const orderBy = "1";
6875
const system = api.ns === "%SYS" ? "1" : "0";
6976
const flat = query.flat && query.flat.length ? query.flat.toString() : "0";
7077
const notStudio = "0";
7178
const generated = query.generated && query.generated.length ? query.generated.toString() : "0";
79+
// get all web apps that have a filepath (Studio dialog used below returns REST ones too)
80+
const cspApps = csp ? await api.getCSPApps().then((data) => data.result.content || []) : [];
81+
const cspSubfolderMap = new Map<string, vscode.FileType>();
82+
const prefix = folder === "" ? "/" : folder;
83+
for (const app of cspApps) {
84+
if ((app + "/").startsWith(prefix)) {
85+
const subfolder = app.slice(prefix.length).split("/")[0];
86+
if (subfolder) {
87+
cspSubfolderMap.set(subfolder, vscode.FileType.Directory);
88+
}
89+
}
90+
}
91+
const cspSubfolders = Array.from(cspSubfolderMap.entries());
92+
// Assemble the results
7293
return api
7394
.actionQuery(sql, [spec, dir, orderBy, system, flat, notStudio, generated])
7495
.then((data) => data.result.content || [])
75-
.then((data) =>
76-
data.map((item: StudioOpenDialog) => {
77-
const name = item.Name;
78-
const fullName = folder === "" ? name : folder + "/" + name;
79-
if (item.Type === "10" || item.Type === "9") {
80-
parent.entries.set(name, new Directory(name, fullName));
81-
return [name, vscode.FileType.Directory];
82-
} else {
83-
return [name, vscode.FileType.File];
84-
}
85-
})
86-
)
96+
.then((data) => {
97+
const results = data
98+
.filter((item: StudioOpenDialog) =>
99+
item.Type === "10"
100+
? csp && !item.Name.includes("/") // ignore web apps here because there may be REST ones
101+
: item.Type === "9" // class package
102+
? !csp
103+
: csp
104+
? item.Type === "5" // web app file
105+
: true
106+
)
107+
.map((item: StudioOpenDialog) => {
108+
const name = item.Name;
109+
const fullName = folder === "" ? name : csp ? folder + name : folder + "/" + name;
110+
if (item.Type === "10" || item.Type === "9") {
111+
parent.entries.set(name, new Directory(name, fullName));
112+
return [name, vscode.FileType.Directory];
113+
} else {
114+
return [name, vscode.FileType.File];
115+
}
116+
});
117+
if (!csp) {
118+
return results;
119+
}
120+
return results.concat(cspSubfolders);
121+
})
87122
.catch((error) => {
88123
error && console.error(error);
89124
});
@@ -138,7 +173,8 @@ export class FileSystemProvider implements vscode.FileSystemProvider {
138173
overwrite: boolean;
139174
}
140175
): void | Thenable<void> {
141-
if (uri.path.match(/\/\.[^/]*\//)) {
176+
uri = redirectDotvscodeRoot(uri);
177+
if (uri.path.startsWith("/.")) {
142178
throw vscode.FileSystemError.NoPermissions("dot-folders not supported by server");
143179
}
144180
const { query } = url.parse(decodeURIComponent(uri.toString()), true);
@@ -235,14 +271,17 @@ export class FileSystemProvider implements vscode.FileSystemProvider {
235271
private async _lookup(uri: vscode.Uri): Promise<Entry> {
236272
const parts = uri.path.split("/");
237273
let entry: Entry = this.root;
238-
for (const part of parts) {
274+
for (let i = 0; i < parts.length; i++) {
275+
const part = parts[i];
239276
if (!part) {
240277
continue;
241278
}
242279
let child: Entry | undefined;
243280
if (entry instanceof Directory) {
244281
child = entry.entries.get(part);
245-
if (!part.includes(".")) {
282+
// If the last element of path is dotted and is one we haven't already cached as a directory
283+
// then it is assumed to be a file.
284+
if (!part.includes(".") || i + 1 < parts.length) {
246285
const fullName = entry.name === "" ? part : entry.fullName + "/" + part;
247286
child = new Directory(part, fullName);
248287
entry.entries.set(part, child);
@@ -274,10 +313,11 @@ export class FileSystemProvider implements vscode.FileSystemProvider {
274313

275314
// Fetch from server and cache it
276315
private async _lookupAsFile(uri: vscode.Uri): Promise<File> {
277-
// Reject attempts to access files in .-folders such as .vscode and .git
278-
if (uri.path.match(/\/\.[^/]*\//)) {
279-
throw vscode.FileSystemError.FileNotFound("dot-folders not supported by server");
316+
uri = redirectDotvscodeRoot(uri);
317+
if (uri.path.startsWith("/.")) {
318+
throw vscode.FileSystemError.NoPermissions("dot-folders not supported by server");
280319
}
320+
281321
const { query } = url.parse(decodeURIComponent(uri.toString()), true);
282322
const csp = query.csp === "" || query.csp === "1";
283323
const fileName = csp ? uri.path : uri.path.slice(1).replace(/\//g, ".");

src/utils/index.ts

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -72,19 +72,19 @@ export function currentFile(document?: vscode.TextDocument): CurrentFile {
7272
!document.fileName ||
7373
!document.languageId ||
7474
!document.languageId.startsWith("objectscript") ||
75-
fileExt.match(/(csp)/i)) // Skip CSP for now, yet
75+
fileExt.match(/(csp)/i)) // Skip local CSPs for now
7676
) {
7777
return null;
7878
}
7979
const eol = document.eol || vscode.EndOfLine.LF;
80-
const uri = document.uri;
80+
const uri = redirectDotvscodeRoot(document.uri);
8181
const content = document.getText();
8282
let name = "";
8383
let ext = "";
8484
const { query } = url.parse(decodeURIComponent(uri.toString()), true);
8585
const csp = query.csp === "" || query.csp === "1";
8686
if (csp) {
87-
name = fileName.replace("\\", "/");
87+
name = uri.path;
8888
} else if (fileExt === "cls") {
8989
const match = content.match(/^Class (%?\w+(?:\.\w+)+)/im);
9090
if (match) {
@@ -333,3 +333,30 @@ export async function terminalWithDocker(): Promise<vscode.Terminal> {
333333
terminal.show(true);
334334
return terminal;
335335
}
336+
337+
/**
338+
* Alter isfs-type uri.path of /.vscode/* files or subdirectories.
339+
* Rewrite `/.vscode/path/to/file` as `/_vscode/XYZ/path/to/file`
340+
* where XYZ comes from the `ns` queryparam of uri.
341+
* Also alter query to specify `ns=%SYS&csp=1`
342+
*
343+
* @returns uri, altered if necessary.
344+
* @throws if `ns` queryparam is missing but required.
345+
*/
346+
export function redirectDotvscodeRoot(uri: vscode.Uri): vscode.Uri {
347+
if (!schemas.includes(uri.scheme)) {
348+
return uri;
349+
}
350+
const dotMatch = uri.path.match(/^\/(\.[^/]*)\/(.*)$/);
351+
if (dotMatch && dotMatch[1] === ".vscode") {
352+
const nsMatch = `&${uri.query}&`.match(/&ns=(.+)&/);
353+
if (!nsMatch) {
354+
throw new Error("No 'ns' query parameter on uri");
355+
}
356+
const namespace = nsMatch[1];
357+
const newQueryString = (("&" + uri.query).replace(`ns=${namespace}`, "ns=%SYS") + "&csp=1").slice(1);
358+
return uri.with({ path: `/_vscode/${namespace}/${dotMatch[2]}`, query: newQueryString });
359+
} else {
360+
return uri;
361+
}
362+
}

0 commit comments

Comments
 (0)