Skip to content

Commit 8e1cdc8

Browse files
authored
Improve exporting (#818)
1 parent d1c09ca commit 8e1cdc8

File tree

5 files changed

+119
-33
lines changed

5 files changed

+119
-33
lines changed

docs/SettingsReference.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ The extensions in the InterSystems ObjectScript Extension Pack provide many sett
4545
| `"objectscript.export.atelier"` | Export source code as Atelier did it, with packages as subfolders. | `boolean` | `true` | |
4646
| `"objectscript.export.category"` | Category of source code to export: `CLS` = classes; `RTN` = routines; `CSP` = csp files; `OTH` = other. Default is `*` = all. | `string` or `object` | `"*"` | |
4747
| `"objectscript.export.dontExportIfNoChanges"` | Do not rewrite the local file if the content is identical to what came from the server. | `boolean` | `false` | |
48-
| `"objectscript.export.filter"` | SQL filter to limit what to export. | `string` | `""` | |
48+
| `"objectscript.export.filter"` | SQL filter to limit what to export. | `string` | `""` | The filter is applied to document names using the [LIKE predicate](https://irisdocs.intersystems.com/irislatest/csp/docbook/DocBook.UI.Page.cls?KEY=RSQL_like) (i.e. `Name LIKE '%filter%'`). |
4949
| `"objectscript.export.folder"` | Folder for exported source code within workspace. | `string` | `"src"` | |
5050
| `"objectscript.export.generated"` | Export generated source code files, such as INTs generated from classes. | `boolean` | `false` | |
5151
| `"objectscript.export.map"` | Map file names before export, with regexp pattern as a key and replacement as a value. | `object` | `{}` | For example, `{ \"%(.*)\": \"_$1\" }` to make % classes or routines use underscore prefix instead. |

package.json

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -233,6 +233,10 @@
233233
{
234234
"command": "vscode-objectscript.showClassDocumentationPreview",
235235
"when": "editorLangId == objectscript-class"
236+
},
237+
{
238+
"command": "vscode-objectscript.exportCurrentFile",
239+
"when": "resourceScheme == objectscript && vscode-objectscript.connectActive"
236240
}
237241
],
238242
"view/title": [
@@ -333,6 +337,11 @@
333337
"command": "vscode-objectscript.compileOnly",
334338
"when": "editorLangId =~ /^objectscript/ && vscode-objectscript.connectActive",
335339
"group": "objectscript@7"
340+
},
341+
{
342+
"command": "vscode-objectscript.exportCurrentFile",
343+
"when": "resourceScheme == objectscript && vscode-objectscript.connectActive",
344+
"group": "objectscript@8"
336345
}
337346
],
338347
"editor/title": [
@@ -497,7 +506,7 @@
497506
{
498507
"category": "ObjectScript",
499508
"command": "vscode-objectscript.export",
500-
"title": "Export Sources",
509+
"title": "Export Code from Server",
501510
"enablement": "!isWeb"
502511
},
503512
{
@@ -695,6 +704,12 @@
695704
"title": "Show Class Documentation Preview",
696705
"enablement": "!isWeb",
697706
"icon": "$(open-preview)"
707+
},
708+
{
709+
"category": "ObjectScript",
710+
"command": "vscode-objectscript.exportCurrentFile",
711+
"title": "Export Current File from Server",
712+
"enablement": "!isWeb"
698713
}
699714
],
700715
"keybindings": [
@@ -895,7 +910,7 @@
895910
"type": "boolean"
896911
},
897912
"filter": {
898-
"description": "SQL filter to limit what to export.",
913+
"markdownDescription": "SQL filter to limit what to export. The filter is applied to document names using the [LIKE predicate](https://irisdocs.intersystems.com/irislatest/csp/docbook/DocBook.UI.Page.cls?KEY=RSQL_like) (i.e. `Name LIKE '%filter%'`).",
899914
"type": "string"
900915
},
901916
"category": {

src/commands/export.ts

Lines changed: 90 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,10 @@ import fs = require("fs");
22
import path = require("path");
33
import * as vscode from "vscode";
44
import { AtelierAPI } from "../api";
5-
import { config, explorerProvider } from "../extension";
6-
import { mkdirSyncRecursive, notNull, outputChannel, uriOfWorkspaceFolder } from "../utils";
5+
import { config, explorerProvider, OBJECTSCRIPT_FILE_SCHEMA, schemas } from "../extension";
6+
import { currentFile, mkdirSyncRecursive, notNull, outputChannel, uriOfWorkspaceFolder } from "../utils";
77
import { NodeBase } from "../explorer/models/nodeBase";
88

9-
const filesFilter = (file: any) => {
10-
if (file.cat === "CSP" || file.name.startsWith("%") || file.name.startsWith("INFORMATION.")) {
11-
return false;
12-
}
13-
return true;
14-
};
15-
169
export const getCategory = (fileName: string, addCategory: any | boolean): string => {
1710
const fileExt = fileName.split(".").pop().toLowerCase();
1811
if (typeof addCategory === "object") {
@@ -216,27 +209,86 @@ export async function exportList(files: string[], workspaceFolder: string, names
216209
);
217210
}
218211

219-
export async function exportAll(workspaceFolder?: string): Promise<any> {
220-
if (!workspaceFolder) {
221-
const list = vscode.workspace.workspaceFolders
222-
.filter((folder) => config("conn", folder.name).active)
223-
.map((el) => el.name);
224-
if (list.length > 1) {
225-
return vscode.window.showQuickPick(list).then((folder) => (folder ? exportAll : null));
226-
} else {
227-
workspaceFolder = list.pop();
212+
export async function exportAll(): Promise<any> {
213+
let workspaceFolder: string;
214+
const workspaceList = vscode.workspace.workspaceFolders
215+
.filter((folder) => !schemas.includes(folder.uri.scheme) && config("conn", folder.name).active)
216+
.map((el) => el.name);
217+
if (workspaceList.length > 1) {
218+
const selection = await vscode.window.showQuickPick(workspaceList, {
219+
placeHolder: "Select the workspace folder to export files to.",
220+
});
221+
if (selection === undefined) {
222+
return;
228223
}
224+
workspaceFolder = selection;
225+
} else if (workspaceList.length === 1) {
226+
workspaceFolder = workspaceList.pop();
227+
} else {
228+
vscode.window.showInformationMessage(
229+
"There are no folders in the current workspace that code can be exported to.",
230+
"Dismiss"
231+
);
232+
return;
229233
}
230234
if (!config("conn", workspaceFolder).active) {
231235
return;
232236
}
233237
const api = new AtelierAPI(workspaceFolder);
234238
outputChannel.show(true);
235-
const { category, generated, filter, ns } = config("export", workspaceFolder);
236-
const files = (data) => data.result.content.filter(filesFilter).map((file) => file.name);
237-
return api.getDocNames({ category, generated, filter }).then((data) => {
238-
return exportList(files(data), workspaceFolder, ns);
239-
});
239+
const { category, generated, filter } = config("export", workspaceFolder);
240+
// Replicate the behavior of getDocNames() but use StudioOpenDialog for better performance
241+
let filterStr = "";
242+
switch (category) {
243+
case "CLS":
244+
filterStr = "Type = 4";
245+
break;
246+
case "CSP":
247+
filterStr = "Type %INLIST $LISTFROMSTRING('5,6')";
248+
break;
249+
case "OTH":
250+
filterStr = "Type NOT %INLIST $LISTFROMSTRING('0,1,2,3,4,5,6,11,12')";
251+
break;
252+
case "RTN":
253+
filterStr = "Type %INLIST $LISTFROMSTRING('0,1,2,3,11,12')";
254+
break;
255+
}
256+
if (filter !== "") {
257+
if (filterStr !== "") {
258+
filterStr += " AND ";
259+
}
260+
filterStr += `Name LIKE '%${filter}%'`;
261+
}
262+
return api
263+
.actionQuery("SELECT Name FROM %Library.RoutineMgr_StudioOpenDialog(?,?,?,?,?,?,?,?)", [
264+
"*",
265+
"1",
266+
"1",
267+
api.config.ns.toLowerCase() === "%sys" ? "1" : "0",
268+
"1",
269+
"0",
270+
generated ? "1" : "0",
271+
filterStr,
272+
])
273+
.then(async (data) => {
274+
let files: vscode.QuickPickItem[] = data.result.content.map((file) => {
275+
return { label: file.Name, picked: true };
276+
});
277+
files = await vscode.window.showQuickPick(files, {
278+
canPickMany: true,
279+
ignoreFocusOut: true,
280+
placeHolder: "Uncheck a file to exclude it. Press 'Escape' to cancel export.",
281+
title: "Files to Export",
282+
});
283+
if (files === undefined) {
284+
return;
285+
}
286+
return exportList(
287+
files.map((file) => file.label),
288+
workspaceFolder,
289+
api.config.ns
290+
);
291+
});
240292
}
241293

242294
export async function exportExplorerItems(nodes: NodeBase[]): Promise<any> {
@@ -264,3 +316,18 @@ Would you like to continue?`,
264316
return exportList(items.flat(), workspaceFolder, namespace).then(() => explorerProvider.refresh());
265317
});
266318
}
319+
320+
export async function exportCurrentFile(): Promise<any> {
321+
const openEditor = vscode.window.activeTextEditor;
322+
if (openEditor === undefined) {
323+
// Need an open document to export
324+
return;
325+
}
326+
const openDoc = openEditor.document;
327+
if (openDoc.uri.scheme !== OBJECTSCRIPT_FILE_SCHEMA) {
328+
// Only export files opened from the explorer
329+
return;
330+
}
331+
const api = new AtelierAPI(openDoc.uri);
332+
return exportList([currentFile(openDoc).name], api.configName, api.config.ns);
333+
}

src/explorer/models/rootNode.ts

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ import { RoutineNode } from "./routineNode";
66
import { AtelierAPI } from "../../api";
77
import { ClassNode } from "./classNode";
88
import { CSPFileNode } from "./cspFileNode";
9-
import { StudioOpenDialog } from "../../queries";
109

1110
type IconPath =
1211
| string
@@ -58,9 +57,12 @@ export class RootNode extends NodeBase {
5857
return this.getItems(path, this._category);
5958
}
6059

61-
public getList(path: string, category: string, flat: boolean): Promise<(StudioOpenDialog & { fullName: string })[]> {
62-
const sql = "CALL %Library.RoutineMgr_StudioOpenDialog(?,?,?,?,?,?,?)";
63-
// const sql = "CALL %Library.RoutineMgr_StudioOpenDialog(?,,,,,,?)";
60+
public getList(
61+
path: string,
62+
category: string,
63+
flat: boolean
64+
): Promise<{ Name: string; Type: string; fullName: string }[]> {
65+
const sql = "SELECT Name, Type FROM %Library.RoutineMgr_StudioOpenDialog(?,?,?,?,?,?,?)";
6466
let spec = "";
6567
switch (category) {
6668
case "CLS":
@@ -102,15 +104,16 @@ export class RootNode extends NodeBase {
102104
return content;
103105
})
104106
.then((data) =>
105-
data.map((el: StudioOpenDialog) => {
107+
data.map((el: { Name: string; Type: number }) => {
106108
let fullName = el.Name;
107109
if (this instanceof PackageNode) {
108110
fullName = this.fullName + "." + el.Name;
109111
} else if (this.isCsp) {
110112
fullName = this.fullName + "/" + el.Name;
111113
}
112114
return {
113-
...el,
115+
Name: el.Name,
116+
Type: String(el.Type),
114117
fullName,
115118
};
116119
})

src/extension.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ import {
2727
compileOnly,
2828
} from "./commands/compile";
2929
import { deleteExplorerItems } from "./commands/delete";
30-
import { exportAll, exportExplorerItems } from "./commands/export";
30+
import { exportAll, exportCurrentFile, exportExplorerItems } from "./commands/export";
3131
import { serverActions } from "./commands/serverActions";
3232
import { subclass } from "./commands/subclass";
3333
import { superclass } from "./commands/superclass";
@@ -933,6 +933,7 @@ export async function activate(context: vscode.ExtensionContext): Promise<any> {
933933
vscode.commands.registerCommand("vscode-objectscript.showClassDocumentationPreview", () =>
934934
DocumaticPreviewPanel.create(context.extensionUri)
935935
),
936+
vscode.commands.registerCommand("vscode-objectscript.exportCurrentFile", exportCurrentFile),
936937

937938
/* Anything we use from the VS Code proposed API */
938939
...proposed

0 commit comments

Comments
 (0)