Skip to content

Commit a0b9f01

Browse files
authored
Merge branch 'main' into fix/screenshot
2 parents 95233ed + 1ffcdef commit a0b9f01

File tree

8 files changed

+143
-10
lines changed

8 files changed

+143
-10
lines changed

CHANGELOG.md

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,25 @@ All notable changes to this project will be documented in this file.
44

55
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). If you introduce breaking changes, please group them together in the "Changed" section using the **BREAKING:** prefix.
66

7-
## [Unreleased]
7+
## [v1.16.0] - 2025-07-31
88

99
### Added
1010

1111
- Add support for `fileNavigationCustomRootPath`/`fileNavigationRoot` for rest & iom/com connections ([#1557](https://github.com/sassoftware/vscode-sas-extension/pull/1557))
12+
- Toggle SAS code comment by line ([#1521](https://github.com/sassoftware/vscode-sas-extension/pull/1521))
13+
- Be able to ignore region from format ([#1534](https://github.com/sassoftware/vscode-sas-extension/pull/1534))
14+
- Set default CSV save location to workspace root for remote workspace ([#1550](https://github.com/sassoftware/vscode-sas-extension/pull/1550))
15+
16+
### Fixed
17+
18+
- Folding does not work in certain case ([#1462](https://github.com/sassoftware/vscode-sas-extension/issues/1462))
19+
- Library viewer optimization for ITC ([#1520](https://github.com/sassoftware/vscode-sas-extension/pull/1520))
20+
- Remove redundant line break at end when format ([#1522](https://github.com/sassoftware/vscode-sas-extension/issues/1522))
21+
- Failed to open a file having character '#' in its name ([#1272](https://github.com/sassoftware/vscode-sas-extension/issues/1272))
22+
- Failed to create and open a file having character "?" in its name ([#1532](https://github.com/sassoftware/vscode-sas-extension/issues/1532))
23+
- Cannot delete a file or folder by keyboard on SAS Explorer pane ([#789](https://github.com/sassoftware/vscode-sas-extension/issues/789))
24+
- Auto-complete for sub-options ([#1555](https://github.com/sassoftware/vscode-sas-extension/issues/1555))
25+
- Toggle line comment in Proc Python ([#1554](https://github.com/sassoftware/vscode-sas-extension/issues/1554))
1226

1327
## [v1.15.0] - 2025-06-10
1428

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-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"name": "sas-lsp",
33
"displayName": "SAS",
44
"description": "Official SAS Language Extension for VS Code",
5-
"version": "1.15.0",
5+
"version": "1.16.0",
66
"categories": [
77
"Programming Languages",
88
"Data Science",
@@ -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",

server/package-lock.json

Lines changed: 4 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

server/src/sas/LanguageServiceProvider.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -246,7 +246,7 @@ export class LanguageServiceProvider {
246246

247247
toggleLineComment(range: Range) {
248248
const token = this.syntaxProvider.getSyntax(range.start.line)[0];
249-
if (token.style === "embedded-code") {
249+
if (token?.style === "embedded-code") {
250250
return null;
251251
}
252252
const lines = this.model

0 commit comments

Comments
 (0)