Skip to content

Commit 05a2759

Browse files
Merge pull request #622 from isc-bsaviano/master
Improve client-side CSP workflow
2 parents 2cecc97 + e7492e8 commit 05a2759

File tree

6 files changed

+122
-45
lines changed

6 files changed

+122
-45
lines changed

package.json

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -230,7 +230,7 @@
230230
"view/item/context": [
231231
{
232232
"command": "vscode-objectscript.explorer.export",
233-
"when": "view == ObjectScriptExplorer && viewItem =~ /^dataNode:(?!(cspApplication|cspFileNode))/"
233+
"when": "view == ObjectScriptExplorer && viewItem =~ /^dataNode:/"
234234
},
235235
{
236236
"command": "vscode-objectscript.explorer.export",
@@ -407,7 +407,8 @@
407407
"ObjectScript CSP"
408408
],
409409
"extensions": [
410-
"csp"
410+
".csp",
411+
".csr"
411412
]
412413
},
413414
{

src/commands/compile.ts

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,9 @@ import {
1111
OBJECTSCRIPT_FILE_SCHEMA,
1212
fileSystemProvider,
1313
workspaceState,
14-
schemas,
1514
} from "../extension";
1615
import { DocumentContentProvider } from "../providers/DocumentContentProvider";
17-
import { currentFile, CurrentFile, outputChannel } from "../utils";
16+
import { currentFile, CurrentFile, currentWorkspaceFolder, outputChannel, workspaceFolderUri } from "../utils";
1817
import { RootNode } from "../explorer/models/rootNode";
1918
import { PackageNode } from "../explorer/models/packageNode";
2019
import { ClassNode } from "../explorer/models/classNode";
@@ -41,7 +40,7 @@ async function compileFlags(): Promise<string> {
4140
* @return mtime timestamp or -1.
4241
*/
4342
export async function checkChangedOnServer(file: CurrentFile, force = false): Promise<number> {
44-
if (!file || !file.uri || schemas.includes(file.uri.scheme)) {
43+
if (!file || !file.uri) {
4544
return -1;
4645
}
4746
const api = new AtelierAPI(file.uri);
@@ -368,20 +367,30 @@ function importFiles(files, noCompile = false) {
368367
}
369368

370369
export async function importFolder(uri: vscode.Uri, noCompile = false): Promise<any> {
371-
const folder = uri.fsPath;
372-
if (fs.lstatSync(folder).isFile()) {
373-
return importFiles([folder], noCompile);
370+
const uripath = uri.fsPath;
371+
if (fs.lstatSync(uripath).isFile()) {
372+
return importFiles([uripath], noCompile);
373+
}
374+
let globpattern = "*.{cls,inc,int,mac}";
375+
const workspace = currentWorkspaceFolder();
376+
const workspacePath = workspaceFolderUri(workspace).fsPath;
377+
const folderPathNoWorkspaceArr = uripath.replace(workspacePath + path.sep, "").split(path.sep);
378+
if (folderPathNoWorkspaceArr.includes("csp")) {
379+
// This folder is a CSP application, so import all files
380+
// We need to include eveything becuase CSP applications can
381+
// include non-InterSystems files
382+
globpattern = "*";
374383
}
375384
glob(
376-
"*.{cls,inc,mac,int}",
385+
globpattern,
377386
{
378-
cwd: folder,
387+
cwd: uripath,
379388
matchBase: true,
380389
nocase: true,
381390
},
382391
(_error, files) =>
383392
importFiles(
384-
files.map((name) => path.join(folder, name)),
393+
files.map((name) => path.join(uripath, name)),
385394
noCompile
386395
)
387396
);

src/commands/export.ts

Lines changed: 54 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ const filesFilter = (file: any) => {
1313
return true;
1414
};
1515

16-
const getCategory = (fileName: string, addCategory: any | boolean): string => {
16+
export const getCategory = (fileName: string, addCategory: any | boolean): string => {
1717
const fileExt = fileName.split(".").pop().toLowerCase();
1818
if (typeof addCategory === "object") {
1919
for (const pattern of Object.keys(addCategory)) {
@@ -45,22 +45,30 @@ export const getFileName = (
4545
[key: string]: string;
4646
}
4747
): string => {
48-
if (map) {
49-
for (const pattern of Object.keys(map)) {
50-
if (new RegExp(`^${pattern}$`).test(name)) {
51-
name = name.replace(new RegExp(`^${pattern}$`), map[pattern]);
52-
break;
48+
if (name.includes("/")) {
49+
// This is a file from a web application
50+
const nameArr: string[] = name.split("/");
51+
const cat = addCategory ? getCategory(name, addCategory) : null;
52+
return [folder, cat, ...nameArr].filter(notNull).join(path.sep);
53+
} else {
54+
// This is a class, routine or include file
55+
if (map) {
56+
for (const pattern of Object.keys(map)) {
57+
if (new RegExp(`^${pattern}$`).test(name)) {
58+
name = name.replace(new RegExp(`^${pattern}$`), map[pattern]);
59+
break;
60+
}
5361
}
5462
}
63+
const fileNameArray: string[] = name.split(".");
64+
const fileExt = fileNameArray.pop().toLowerCase();
65+
const cat = addCategory ? getCategory(name, addCategory) : null;
66+
if (split) {
67+
const fileName = [folder, cat, ...fileNameArray].filter(notNull).join(path.sep);
68+
return [fileName, fileExt].join(".");
69+
}
70+
return [folder, cat, name].filter(notNull).join(path.sep);
5571
}
56-
const fileNameArray: string[] = name.split(".");
57-
const fileExt = fileNameArray.pop().toLowerCase();
58-
const cat = addCategory ? getCategory(name, addCategory) : null;
59-
if (split) {
60-
const fileName = [folder, cat, ...fileNameArray].filter(notNull).join(path.sep);
61-
return [fileName, fileExt].join(".");
62-
}
63-
return [folder, cat, name].filter(notNull).join(path.sep);
6472
};
6573

6674
export const getFolderName = (folder: string, name: string, split: boolean, cat: string = null): string => {
@@ -122,26 +130,43 @@ export async function exportFile(
122130

123131
return promise
124132
.then((res: any) => {
125-
let joinedContent = content instanceof Buffer ? content.toString("utf-8") : (content || []).join("\n");
126-
let isSkipped = "";
133+
if (Buffer.isBuffer(content)) {
134+
// This is a binary file
135+
let isSkipped = "";
136+
if (dontExportIfNoChanges && fs.existsSync(fileName)) {
137+
const existingContent = fs.readFileSync(fileName);
138+
if (content.equals(existingContent)) {
139+
fs.writeFileSync(fileName, content);
140+
} else {
141+
isSkipped = " => skipped - no changes.";
142+
}
143+
} else {
144+
fs.writeFileSync(fileName, content);
145+
}
146+
log(`Success ${isSkipped}`);
147+
} else {
148+
// This is a text file
149+
let joinedContent = content.join("\n");
150+
let isSkipped = "";
127151

128-
if (res.found) {
129-
joinedContent = res.content.toString("utf8");
130-
}
152+
if (res.found) {
153+
joinedContent = res.content.toString("utf8");
154+
}
131155

132-
if (dontExportIfNoChanges && fs.existsSync(fileName)) {
133-
const existingContent = fs.readFileSync(fileName, "utf8");
134-
// stringify to harmonise the text encoding.
135-
if (JSON.stringify(joinedContent) !== JSON.stringify(existingContent)) {
136-
fs.writeFileSync(fileName, joinedContent);
156+
if (dontExportIfNoChanges && fs.existsSync(fileName)) {
157+
const existingContent = fs.readFileSync(fileName, "utf8");
158+
// stringify to harmonise the text encoding.
159+
if (JSON.stringify(joinedContent) !== JSON.stringify(existingContent)) {
160+
fs.writeFileSync(fileName, joinedContent);
161+
} else {
162+
isSkipped = " => skipped - no changes.";
163+
}
137164
} else {
138-
isSkipped = " => skipped - no changes.";
165+
fs.writeFileSync(fileName, joinedContent);
139166
}
140-
} else {
141-
fs.writeFileSync(fileName, joinedContent);
142-
}
143167

144-
log(`Success ${isSkipped}`);
168+
log(`Success ${isSkipped}`);
169+
}
145170
})
146171
.catch((error) => {
147172
throw error;

src/explorer/models/rootNode.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -155,7 +155,8 @@ export class RootNode extends NodeBase {
155155
}
156156

157157
public getItems4Export(): Promise<string[]> {
158-
const path = this instanceof PackageNode ? this.fullName + "/" : "";
159-
return this.getList(path, "ALL", true).then((data) => data.map((el) => el.Name));
158+
const path = this instanceof PackageNode || this.isCsp ? this.fullName + "/" : "";
159+
const cat = this.isCsp ? "CSP" : "ALL";
160+
return this.getList(path, cat, true).then((data) => data.map((el) => el.Name));
160161
}
161162
}

src/extension.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ import {
7676
notNull,
7777
currentFile,
7878
InputBoxManager,
79+
isImportableLocalFile,
7980
} from "./utils";
8081
import { ObjectScriptDiagnosticProvider } from "./providers/ObjectScriptDiagnosticProvider";
8182
import { DocumentRangeFormattingEditProvider } from "./providers/DocumentRangeFormattingEditProvider";
@@ -537,6 +538,12 @@ export async function activate(context: vscode.ExtensionContext): Promise<any> {
537538
if (documentBeingProcessed !== file) {
538539
return importAndCompile(false, file, config("compileOnSave"));
539540
}
541+
} else if (file.uri.scheme === "file") {
542+
if (isImportableLocalFile(file)) {
543+
// This local file is in the exported file tree, so it's a non-InterSystems file that's
544+
// part of a CSP application, so import it on save
545+
return importFileOrFolder(file.uri, true);
546+
}
540547
}
541548
});
542549

src/utils/index.ts

Lines changed: 37 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,29 @@ export interface ConnectionTarget {
5555
configName: string;
5656
}
5757

58+
/**
59+
* Determine the server name of a local non-ObjectScript file (any file that's not CLS,MAC,INT,INC).
60+
* @param localPath The full path to the file on disk.
61+
* @param workspace The workspace the file is in.
62+
*/
63+
function getServerDocName(localPath: string, workspace: string): string {
64+
const workspacePath = workspaceFolderUri(workspace).fsPath;
65+
const filePathNoWorkspaceArr = localPath.replace(workspacePath + path.sep, "").split(path.sep);
66+
return filePathNoWorkspaceArr.slice(filePathNoWorkspaceArr.indexOf("csp")).join("/");
67+
}
68+
69+
/**
70+
* Determine if this non-InterSystems local file is importable
71+
* (i.e. is part of a CSP application).
72+
* @param file The file to check.
73+
*/
74+
export function isImportableLocalFile(file: vscode.TextDocument): boolean {
75+
const workspace = currentWorkspaceFolder(file);
76+
const workspacePath = workspaceFolderUri(workspace).fsPath;
77+
const filePathNoWorkspaceArr = file.fileName.replace(workspacePath + path.sep, "").split(path.sep);
78+
return filePathNoWorkspaceArr.includes("csp");
79+
}
80+
5881
export function currentFile(document?: vscode.TextDocument): CurrentFile {
5982
document =
6083
document ||
@@ -72,9 +95,12 @@ export function currentFile(document?: vscode.TextDocument): CurrentFile {
7295
!document.fileName ||
7396
!document.languageId ||
7497
!document.languageId.startsWith("objectscript") ||
75-
fileExt.match(/(csp)/i)) // Skip local CSPs for now
98+
document.languageId === "objectscript-output")
7699
) {
77-
return null;
100+
// This is a non-InterSystems local file, so check if we can import it
101+
if (!isImportableLocalFile(document)) {
102+
return null;
103+
}
78104
}
79105
const eol = document.eol || vscode.EndOfLine.LF;
80106
const uri = redirectDotvscodeRoot(document.uri);
@@ -99,7 +125,15 @@ export function currentFile(document?: vscode.TextDocument): CurrentFile {
99125
[name, ext = "mac"] = path.basename(document.fileName).split(".");
100126
}
101127
} else {
102-
name = fileName;
128+
if (document.uri.scheme === "file") {
129+
if (fileExt.match(/(csp|csr)/i) && !isImportableLocalFile(document)) {
130+
// This is a csp or csr file that's not in a csp directory
131+
return null;
132+
}
133+
name = getServerDocName(fileName, currentWorkspaceFolder(document));
134+
} else {
135+
name = fileName;
136+
}
103137
// Need to strip leading / for custom Studio documents which should not be treated as files.
104138
// e.g. For a custom Studio document Test.ZPM, the variable name would be /Test.ZPM which is
105139
// not the document name. The document name is Test.ZPM so requests made to the Atelier APIs

0 commit comments

Comments
 (0)