Skip to content

Commit 1ffcdef

Browse files
kpatl1Kishan Patel
andauthored
added ability to save cell output (#1576)
* added ability to export cell output * added support for output only to be exported. * added the option for the user to choose HTML, HTML + Log. * changes to export only ODS and Log * fixed feature to meet specs. * fixes * references name changing so export works. * comment resolution. * comment resolution. * DCO Remediation Commit for Kishan Patel <[email protected]> I, Kishan Patel <[email protected]>, hereby add my Signed-off-by to this commit: feaa5e9 I, Kishan Patel <[email protected]>, hereby add my Signed-off-by to this commit: 2711500 I, Kishan Patel <[email protected]>, hereby add my Signed-off-by to this commit: 8da0a71 I, Kishan Patel <[email protected]>, hereby add my Signed-off-by to this commit: 39a4585 I, Kishan Patel <[email protected]>, hereby add my Signed-off-by to this commit: 47f30ea I, Kishan Patel <[email protected]>, hereby add my Signed-off-by to this commit: 27f60fb I, Kishan Patel <[email protected]>, hereby add my Signed-off-by to this commit: b770fef I, Kishan Patel <[email protected]>, hereby add my Signed-off-by to this commit: dc0338c I, Kishan Patel <[email protected]>, hereby add my Signed-off-by to this commit: a936bb8 Signed-off-by: Kishan Patel <[email protected]> * comment resolution. * DCO Remediation Commit for Kishan Patel <[email protected]> I, Kishan Patel <[email protected]>, hereby add my Signed-off-by to this commit: 6ed0193 Signed-off-by: Kishan Patel <[email protected]> --------- Signed-off-by: Kishan Patel <[email protected]> Signed-off-by: Kishan Patel <[email protected]> Co-authored-by: Kishan Patel <[email protected]>
1 parent a5b6237 commit 1ffcdef

File tree

4 files changed

+120
-2
lines changed

4 files changed

+120
-2
lines changed

client/src/components/notebook/exporters/index.ts

Lines changed: 106 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
// Copyright © 2025, SAS Institute Inc., Cary, NC, USA. All Rights Reserved.
22
// SPDX-License-Identifier: Apache-2.0
3-
import { Uri, window, workspace } from "vscode";
3+
import { Uri, l10n, window, workspace } from "vscode";
44
import type { LanguageClient } from "vscode-languageclient/node";
55

66
import path from "path";
@@ -30,3 +30,108 @@ export const exportNotebook = async (client: LanguageClient) => {
3030

3131
workspace.fs.writeFile(uri, Buffer.from(content));
3232
};
33+
34+
export const saveOutput = async () => {
35+
const notebook = window.activeNotebookEditor?.notebook;
36+
const activeCell = window.activeNotebookEditor?.selection?.start;
37+
38+
if (!notebook || activeCell === undefined) {
39+
return;
40+
}
41+
42+
const cell = notebook.cellAt(activeCell);
43+
if (!cell) {
44+
return;
45+
}
46+
47+
let odsItem = null;
48+
let logItem = null;
49+
50+
for (const output of cell.outputs) {
51+
if (!odsItem) {
52+
odsItem = output.items.find(
53+
(item) => item.mime === "application/vnd.sas.ods.html5",
54+
);
55+
}
56+
if (!logItem) {
57+
logItem = output.items.find(
58+
(item) => item.mime === "application/vnd.sas.compute.log.lines",
59+
);
60+
}
61+
62+
if (odsItem && logItem) {
63+
break;
64+
}
65+
}
66+
67+
const choices: Array<{
68+
label: string;
69+
outputType: "html" | "log";
70+
}> = [];
71+
72+
if (odsItem) {
73+
choices.push({
74+
label: l10n.t("Save ODS HTML"),
75+
outputType: "html",
76+
});
77+
}
78+
79+
if (logItem) {
80+
choices.push({
81+
label: l10n.t("Save Log"),
82+
outputType: "log",
83+
});
84+
}
85+
86+
const exportChoice = await window.showQuickPick(choices, {
87+
placeHolder: l10n.t("Choose output type to save"),
88+
ignoreFocusOut: true,
89+
});
90+
91+
if (!exportChoice) {
92+
return;
93+
}
94+
95+
let content = "";
96+
let fileExtension = "";
97+
let fileName = "";
98+
try {
99+
if (exportChoice.outputType === "html" && odsItem) {
100+
content = odsItem.data.toString();
101+
fileExtension = "html";
102+
fileName = `${path.basename(notebook.uri.path, ".sasnb")}_${l10n.t("output")}_${
103+
activeCell + 1
104+
}.html`;
105+
} else if (exportChoice.outputType === "log" && logItem) {
106+
const logs: Array<{ line: string; type: string }> = JSON.parse(
107+
logItem.data.toString(),
108+
);
109+
content = logs.map((log) => log.line).join("\n");
110+
fileExtension = "log";
111+
fileName = `${path.basename(notebook.uri.path, ".sasnb")}_${l10n.t("output")}_${
112+
activeCell + 1
113+
}.log`;
114+
}
115+
} catch (error) {
116+
window.showErrorMessage(
117+
l10n.t("Failed to extract output content." + error),
118+
);
119+
return;
120+
}
121+
122+
const filters: { [name: string]: string[] } = {};
123+
filters[fileExtension.toUpperCase()] = [fileExtension];
124+
125+
const uri = await window.showSaveDialog({
126+
filters,
127+
defaultUri: Uri.parse(fileName),
128+
});
129+
130+
if (!uri) {
131+
return;
132+
}
133+
134+
await workspace.fs.writeFile(uri, Buffer.from(content));
135+
136+
window.showInformationMessage(l10n.t("Saved to {0}", uri.fsPath));
137+
};

client/src/node/extension.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ import { LogTokensProvider, legend } from "../components/logViewer";
5454
import { sasDiagnostic } from "../components/logViewer/sasDiagnostics";
5555
import { NotebookController } from "../components/notebook/Controller";
5656
import { NotebookSerializer } from "../components/notebook/Serializer";
57-
import { exportNotebook } from "../components/notebook/exporters";
57+
import { exportNotebook, saveOutput } from "../components/notebook/exporters";
5858
import { ConnectionType } from "../components/profile";
5959
import { SasTaskProvider } from "../components/tasks/SasTaskProvider";
6060
import { SAS_TASK_TYPE } from "../components/tasks/SasTasks";
@@ -202,6 +202,7 @@ export function activate(context: ExtensionContext) {
202202
commands.registerCommand("SAS.notebook.export", () =>
203203
exportNotebook(client),
204204
),
205+
commands.registerCommand("SAS.notebook.saveOutput", saveOutput),
205206
tasks.registerTaskProvider(SAS_TASK_TYPE, new SasTaskProvider()),
206207
...sasDiagnostic.getSubscriptions(),
207208
commands.registerTextEditorCommand("SAS.toggleLineComment", (editor) => {

package.json

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -825,6 +825,11 @@
825825
"title": "%commands.SAS.notebook.export%",
826826
"category": "SAS Notebook"
827827
},
828+
{
829+
"command": "SAS.notebook.saveOutput",
830+
"title": "%commands.SAS.notebook.saveOutput%",
831+
"category": "SAS Notebook"
832+
},
828833
{
829834
"command": "SAS.file.new",
830835
"shortTitle": "%commands.SAS.file.new.short%",
@@ -1097,6 +1102,12 @@
10971102
"command": "SAS.notebook.export"
10981103
}
10991104
],
1105+
"notebook/cell/title": [
1106+
{
1107+
"command": "SAS.notebook.saveOutput",
1108+
"when": "notebookType == 'sas-notebook' && notebookCellHasOutputs"
1109+
}
1110+
],
11001111
"commandPalette": [
11011112
{
11021113
"when": "editorLangId == sas && !SAS.hideRunMenuItem",

package.nls.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
"commands.SAS.file.new": "New SAS File",
1717
"commands.SAS.file.new.short": "SAS File",
1818
"commands.SAS.notebook.export": "Export",
19+
"commands.SAS.notebook.saveOutput": "Save Output",
1920
"commands.SAS.notebook.new": "New SAS Notebook",
2021
"commands.SAS.notebook.new.short": "SAS Notebook",
2122
"commands.SAS.refresh": "Refresh",

0 commit comments

Comments
 (0)