From 416922de21edd08151dcf41a720c58ff4359efc5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elijah=20C=C3=BAchulainn=20Reid?= <56865341+Wizzzzzzard@users.noreply.github.com> Date: Thu, 4 Dec 2025 16:59:42 +0000 Subject: [PATCH 01/34] initial commit of proc_r embedded lang test --- server/test/embedded_lang/embedded_lang.test.ts | 16 ++++++++++++++++ server/testFixture/embedded_lang/proc_rlang.sas | 8 ++++++++ 2 files changed, 24 insertions(+) create mode 100644 server/testFixture/embedded_lang/proc_rlang.sas diff --git a/server/test/embedded_lang/embedded_lang.test.ts b/server/test/embedded_lang/embedded_lang.test.ts index b8c1b3ed7..9fcf14873 100644 --- a/server/test/embedded_lang/embedded_lang.test.ts +++ b/server/test/embedded_lang/embedded_lang.test.ts @@ -64,4 +64,20 @@ describe("Test code zone for embedded language", () => { assert.equal(zoneList[4], CodeZoneManager.ZONE_TYPE.EMBEDDED_LANG); assert.equal(zoneList[6], CodeZoneManager.ZONE_TYPE.PROC_STMT); }); + + it("proc rlang", () => { + const doc = openDoc("server/testFixture/embedded_lang/proc_r.sas"); + const languageServer = new LanguageServiceProvider(doc); + const codeZoneManager = languageServer.getCodeZoneManager(); + + const zoneList = []; + for (let i = 0; i < doc.lineCount; i++) { + zoneList.push(codeZoneManager.getCurrentZone(i, 1)); + } + + assert.equal(zoneList[1], CodeZoneManager.ZONE_TYPE.PROC_STMT); + assert.equal(zoneList[2], CodeZoneManager.ZONE_TYPE.EMBEDDED_LANG); + assert.equal(zoneList[4], CodeZoneManager.ZONE_TYPE.EMBEDDED_LANG); + assert.equal(zoneList[6], CodeZoneManager.ZONE_TYPE.PROC_STMT); + }); }); diff --git a/server/testFixture/embedded_lang/proc_rlang.sas b/server/testFixture/embedded_lang/proc_rlang.sas new file mode 100644 index 000000000..29462e8b2 --- /dev/null +++ b/server/testFixture/embedded_lang/proc_rlang.sas @@ -0,0 +1,8 @@ +proc rlang; + submit; + for (x in 1:6) { + print(x) + } + print("first statement after for loop") + endsubmit; +run; From 74a5d313b26664676679882063e72355c1b71bab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elijah=20C=C3=BAchulainn=20Reid?= <56865341+Wizzzzzzard@users.noreply.github.com> Date: Fri, 5 Dec 2025 09:50:13 +0000 Subject: [PATCH 02/34] adding R language options to client and notebook --- client/package-lock.json | 3 +++ .../src/components/ContentNavigator/convert.ts | 4 +++- client/src/components/notebook/Controller.ts | 2 +- .../src/components/notebook/exporters/toHTML.ts | 2 ++ .../src/components/notebook/exporters/toSAS.ts | 8 ++++++++ client/src/components/utils/SASCodeDocument.ts | 12 ++++++++++++ package-lock.json | 17 +++++++++++++++-- package.json | 3 ++- 8 files changed, 46 insertions(+), 5 deletions(-) diff --git a/client/package-lock.json b/client/package-lock.json index 8dc506818..60a83aeb9 100644 --- a/client/package-lock.json +++ b/client/package-lock.json @@ -47,6 +47,7 @@ "integrity": "sha512-MWtvHrGZLFttgeEj28VXHxpmwYbor/ATPYbBfSFZEIRK0ecCFLl2Qo55z52Hss+UV9CRN7trSeq1zbgx7YDWWg==", "devOptional": true, "license": "MIT", + "peer": true, "dependencies": { "csstype": "^3.2.2" } @@ -865,6 +866,7 @@ "resolved": "https://registry.npmjs.org/react/-/react-19.2.0.tgz", "integrity": "sha512-tmbWg6W31tQLeB5cdIBOicJDJRR2KzXsV7uSK9iNfLWQ5bIZfxuPEHp7M8wiHyHnn0DD1i7w3Zmin0FtkrwoCQ==", "license": "MIT", + "peer": true, "engines": { "node": ">=0.10.0" } @@ -874,6 +876,7 @@ "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.0.tgz", "integrity": "sha512-UlbRu4cAiGaIewkPyiRGJk0imDN2T3JjieT6spoL2UeSf5od4n5LB/mQ4ejmxhCFT1tYe8IvaFulzynWovsEFQ==", "license": "MIT", + "peer": true, "dependencies": { "scheduler": "^0.27.0" }, diff --git a/client/src/components/ContentNavigator/convert.ts b/client/src/components/ContentNavigator/convert.ts index 4a8783e2a..80a03568e 100644 --- a/client/src/components/ContentNavigator/convert.ts +++ b/client/src/components/ContentNavigator/convert.ts @@ -20,12 +20,14 @@ const stepRef: Record = { sas: "a7190700-f59c-4a94-afe2-214ce639fcde", sql: "a7190700-f59c-4a94-afe2-214ce639fcde", python: "ab59f8c4-af9a-4608-a5d5-a8365357bb99", + rlang: "ab59f8c4-af9a-4608-a5d5-a8365357bb99", }; const stepTitle: Record = { sas: l10n.t("SAS Program"), sql: l10n.t("SQL Program"), python: l10n.t("Python Program"), + rlang: l10n.t("R Program"), }; const NODE_SPACING = 150; @@ -305,7 +307,7 @@ function generateCodeListFromSASNotebook(content: string): Entry[] { let code = cell.value; if (code !== "") { const language = cell.language; - if (["python", "sas", "sql"].includes(language)) { + if (["python", "sas", "sql", "rlang"].includes(language)) { if (language === "sql") { code = `PROC SQL; ${code}; diff --git a/client/src/components/notebook/Controller.ts b/client/src/components/notebook/Controller.ts index c23bab5b7..01c1cfa1d 100644 --- a/client/src/components/notebook/Controller.ts +++ b/client/src/components/notebook/Controller.ts @@ -11,7 +11,7 @@ export class NotebookController { readonly controllerId = "sas-notebook-controller-id"; readonly notebookType = "sas-notebook"; readonly label = "SAS Notebook"; - readonly supportedLanguages = ["sas", "sql", "python"]; + readonly supportedLanguages = ["sas", "sql", "python", "rlang"]; private readonly _controller: vscode.NotebookController; private _executionOrder = 0; diff --git a/client/src/components/notebook/exporters/toHTML.ts b/client/src/components/notebook/exporters/toHTML.ts index c8c910eae..beb79a704 100644 --- a/client/src/components/notebook/exporters/toHTML.ts +++ b/client/src/components/notebook/exporters/toHTML.ts @@ -17,6 +17,7 @@ import { import { readFileSync } from "fs"; import hljs from "highlight.js/lib/core"; import python from "highlight.js/lib/languages/python"; +import r from "highlight.js/lib/languages/r"; import sql from "highlight.js/lib/languages/sql"; import { marked } from "marked"; import path from "path"; @@ -27,6 +28,7 @@ import { includeLogInNotebookExport } from "../../utils/settings"; const templatesDir = path.resolve(__dirname, "../notebook/exporters/templates"); hljs.registerLanguage("python", python); +hljs.registerLanguage("rlang", r); hljs.registerLanguage("sql", sql); export const exportToHTML = async ( diff --git a/client/src/components/notebook/exporters/toSAS.ts b/client/src/components/notebook/exporters/toSAS.ts index 9bd136054..3543a2b07 100644 --- a/client/src/components/notebook/exporters/toSAS.ts +++ b/client/src/components/notebook/exporters/toSAS.ts @@ -15,6 +15,8 @@ const exportCell = (cell: NotebookCell) => { return text; case "python": return wrapPython(text); + case "rlang": + return wrapRlang(text); case "sql": return wrapSQL(text); case "markdown": @@ -36,3 +38,9 @@ submit; ${code} endsubmit; run;`; + +const wrapRlang = (code: string) => `proc rlang; +submit; +${code} +endsubmit; +run;`; diff --git a/client/src/components/utils/SASCodeDocument.ts b/client/src/components/utils/SASCodeDocument.ts index 4ab4b054d..fba1e54a0 100644 --- a/client/src/components/utils/SASCodeDocument.ts +++ b/client/src/components/utils/SASCodeDocument.ts @@ -180,6 +180,14 @@ endsubmit; run;`; } + private wrapRlang(code: string) { + return `proc rlang; +submit; +${code} +endsubmit; +run;`; + } + private insertLogStartIndicator(code: string): string { // add a comment line at the top of code, // this comment line will be used as indicator to the beginning of log related with this code @@ -198,6 +206,10 @@ ${code}`; wrapped = this.wrapPython(wrapped); } + if (this.parameters.languageId === "rlang") { + wrapped = this.wrapRlang(wrapped); + } + wrapped = this.wrapCodeWithSASProgramFileName(wrapped); wrapped = this.wrapCodeWithPreambleAndPostamble(wrapped); diff --git a/package-lock.json b/package-lock.json index 95217ee7c..2d726c809 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "sas-lsp", - "version": "1.17.0", + "version": "1.18.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "sas-lsp", - "version": "1.17.0", + "version": "1.18.0", "hasInstallScript": true, "license": "Apache-2.0", "devDependencies": { @@ -200,6 +200,7 @@ "integrity": "sha512-2BCOP7TN8M+gVDj7/ht3hsaO/B/n5oDbiAyyvnRlNOs+u1o+JWNYTQrmpuNp1/Wq2gcFrI01JAW+paEKDMx/CA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/generator": "^7.28.3", @@ -1557,6 +1558,7 @@ "integrity": "sha512-GNWcUTRBgIRJD5zj+Tq0fKOJ5XZajIiBroOF0yvj2bSU1WvNdYS/dn9UxwsujGW4JX06dnHyjV2y9rRaybH0iQ==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "undici-types": "~7.16.0" } @@ -1633,6 +1635,7 @@ "integrity": "sha512-jCzKdm/QK0Kg4V4IK/oMlRZlY+QOcdjv89U2NgKHZk1CYTj82/RVSx1mV/0gqCVMJ/DA+Zf/S4NBWNF8GQ+eqQ==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@typescript-eslint/scope-manager": "8.48.0", "@typescript-eslint/types": "8.48.0", @@ -2157,6 +2160,7 @@ "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", "dev": true, "license": "MIT", + "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -2519,6 +2523,7 @@ } ], "license": "MIT", + "peer": true, "dependencies": { "baseline-browser-mapping": "^2.8.9", "caniuse-lite": "^1.0.30001746", @@ -3410,6 +3415,7 @@ "integrity": "sha512-BhHmn2yNOFA9H9JmmIVKJmd288g9hrVRDkdoIgRCRuSySRUHH7r/DI6aAXW9T1WwUuY3DFgrcaqB+deURBLR5g==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.1", @@ -5731,6 +5737,7 @@ "integrity": "sha512-QgODejq9K3OzoBbuyobZlUhznP5SKwPqp+6Q6xw6o8gnhr4O85L2U915iM2IDcfF2NPXVaM9zlo9tdwipnYwzg==", "dev": true, "license": "MIT", + "peer": true, "bin": { "prettier": "bin/prettier.cjs" }, @@ -6075,6 +6082,7 @@ "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", @@ -6618,6 +6626,7 @@ "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=12" }, @@ -6942,6 +6951,7 @@ "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.5.3.tgz", "integrity": "sha512-/hreyEujaB0w76zKo6717l3L0o/qEUtRgdvUBvlkhoWeOVMjMuHNHk0BRBzikzuGDqNmPQbg5ifMEqsHLiIUcQ==", "dev": true, + "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -7052,6 +7062,7 @@ "integrity": "sha512-HU1JOuV1OavsZ+mfigY0j8d1TgQgbZ6M+J75zDkpEAwYeXjWSqrGJtgnPblJjd/mAyTNQ7ygw0MiKOn6etz8yw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@types/eslint-scope": "^3.7.7", "@types/estree": "^1.0.8", @@ -7100,6 +7111,7 @@ "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-6.0.1.tgz", "integrity": "sha512-MfwFQ6SfwinsUVi0rNJm7rHZ31GyTcpVE5pgVA3hwFRb7COD4TzjUUwhGWKfO50+xdc2MQPuEBBJoqIMGt3JDw==", "dev": true, + "peer": true, "dependencies": { "@discoveryjs/json-ext": "^0.6.1", "@webpack-cli/configtest": "^3.0.1", @@ -7428,6 +7440,7 @@ "integrity": "sha512-JInaHOamG8pt5+Ey8kGmdcAcg3OL9reK8ltczgHTAwNhMys/6ThXHityHxVV2p3fkw/c+MAvBHFVYHFZDmjMCQ==", "dev": true, "license": "MIT", + "peer": true, "funding": { "url": "https://github.com/sponsors/colinhacks" } diff --git a/package.json b/package.json index e0af38778..7146789fc 100644 --- a/package.json +++ b/package.json @@ -1296,7 +1296,8 @@ "embeddedLanguages": { "source.python": "python", "source.lua": "lua", - "source.sql": "sql" + "source.sql": "sql", + "source.r": "r" } } ], From 0231587713a8e85e4aaad921cf10d8e03862835d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elijah=20C=C3=BAchulainn=20Reid?= <56865341+Wizzzzzzard@users.noreply.github.com> Date: Fri, 5 Dec 2025 09:50:36 +0000 Subject: [PATCH 03/34] adding some R Code test fixtures --- client/testFixture/sasnb_export.sas | 17 +++++++++++++++++ client/testFixture/sasnb_export.sasnb | 2 +- 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/client/testFixture/sasnb_export.sas b/client/testFixture/sasnb_export.sas index 088afec00..6b436bff7 100644 --- a/client/testFixture/sasnb_export.sas +++ b/client/testFixture/sasnb_export.sas @@ -19,6 +19,23 @@ print("Result: ", a*10 + b) endsubmit; run; +/* +## R Code + +This is some R code +*/ + +/* +This is a separate note in **Markdown** format. +*/ + +proc rlang; +submit; +die <- 1:6 +paste("Die Maths: ", die[3]*4 + die[6]) +endsubmit; +run; + /* ## SAS Code */ diff --git a/client/testFixture/sasnb_export.sasnb b/client/testFixture/sasnb_export.sasnb index 7ed0c4ad8..f1167285b 100644 --- a/client/testFixture/sasnb_export.sasnb +++ b/client/testFixture/sasnb_export.sasnb @@ -1 +1 @@ -[{"kind":1,"language":"markdown","value":"# Notebook to SAS Test","outputs":[]},{"kind":1,"language":"markdown","value":"## Python Code\n\nThis is some Python code","outputs":[]},{"kind":1,"language":"markdown","value":"This is a separate note in **Markdown** format.","outputs":[]},{"kind":2,"language":"python","value":"a, b = 4, 2\nprint(\"Result: \", a*10 + b)","outputs":[{"items":[{"data":"[\n\t{\n\t\t\"line\": \"44 ods html5;\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"NOTE: Writing HTML5 Body file: sashtml4.htm\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"45 proc python;\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"46 submit\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"NOTE: Resuming Python state from previous PROC PYTHON invocation.\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"46 ! ;\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"47 a, b = 4, 2\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"48 print(\\\"Result: \\\", a*10 + b)\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"49 endsubmit;\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"50 run;\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \">>>\",\n\t\t\"type\": \"normal\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"Result: 42\",\n\t\t\"type\": \"normal\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \">>> \",\n\t\t\"type\": \"normal\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"NOTE: PROCEDURE PYTHON used (Total process time):\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \" real time 0.00 seconds\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \" cpu time 0.00 seconds\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \" \",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"51 ;run;quit;ods html5 close;\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t}\n]","mime":"application/vnd.sas.compute.log.lines"}]}]},{"kind":1,"language":"markdown","value":"## SAS Code","outputs":[]},{"kind":2,"language":"sas","value":"data work.prdsale;\n\tset sashelp.PRDSALE;\nrun;\n\nproc means data=work.prdsale;\nrun;","outputs":[{"items":[{"data":"\n\n\n\n\nSAS Output\n\n\n\n
\n
\n

The SAS System

\n
\n
\n

The MEANS Procedure

\n
\n
\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
VariableLabelNMeanStd DevMinimumMaximum
\n
\n
ACTUAL
\n
PREDICT
\n
QUARTER
\n
YEAR
\n
MONTH
\n
\n
\n
\n
Actual Sales
\n
Predicted Sales
\n
Quarter
\n
Year
\n
Month
\n
\n
\n
\n
1440
\n
1440
\n
1440
\n
1440
\n
1440
\n
\n
\n
\n
507.1784722
\n
490.4826389
\n
2.5000000
\n
1993.50
\n
12403.00
\n
\n
\n
\n
287.0313065
\n
285.7667904
\n
1.1184224
\n
0.5001737
\n
210.6291578
\n
\n
\n
\n
3.0000000
\n
0
\n
1.0000000
\n
1993.00
\n
12054.00
\n
\n
\n
\n
1000.00
\n
1000.00
\n
4.0000000
\n
1994.00
\n
12753.00
\n
\n
\n
\n
\n\n\n","mime":"application/vnd.sas.ods.html5"},{"data":"[\n\t{\n\t\t\"line\": \"16 ods html5;\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"NOTE: Writing HTML5 Body file: sashtml1.htm\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"17 data work.prdsale;\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"18 \\tset sashelp.PRDSALE;\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"19 run;\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"NOTE: There were 1440 observations read from the data set SASHELP.PRDSALE.\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"NOTE: The data set WORK.PRDSALE has 1440 observations and 10 variables.\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"NOTE: DATA statement used (Total process time):\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \" real time 0.00 seconds\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \" cpu time 0.01 seconds\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \" \",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"20 \",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"21 proc means data=work.prdsale;\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"22 run;\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"NOTE: There were 1440 observations read from the data set WORK.PRDSALE.\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"NOTE: The PROCEDURE MEANS printed page 1.\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"NOTE: PROCEDURE MEANS used (Total process time):\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \" real time 0.04 seconds\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \" cpu time 0.05 seconds\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \" \",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"23 ;run;quit;ods html5 close;\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t}\n]","mime":"application/vnd.sas.compute.log.lines"}]}]},{"kind":1,"language":"markdown","value":"## SQL Code","outputs":[]},{"kind":2,"language":"sql","value":"CREATE TABLE WORK.QUERY_PRDSALE AS\n SELECT\n (t1.COUNTRY) LABEL='Country' FORMAT=$CHAR10.,\n (SUM(t1.ACTUAL)) FORMAT=DOLLAR12.2 LENGTH=8 AS SUM_ACTUAL\n FROM\n WORK.PRDSALE t1\n GROUP BY\n t1.COUNTRY","outputs":[{"items":[{"data":"[\n\t{\n\t\t\"line\": \"24 ods html5;\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"NOTE: Writing HTML5 Body file: sashtml2.htm\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"25 proc sql;\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"26 CREATE TABLE WORK.QUERY_PRDSALE AS\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"27 SELECT\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"28 (t1.COUNTRY) LABEL='Country' FORMAT=$CHAR10.,\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"29 (SUM(t1.ACTUAL)) FORMAT=DOLLAR12.2 LENGTH=8 AS SUM_ACTUAL\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"30 FROM\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"31 WORK.PRDSALE t1\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"32 GROUP BY\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"33 t1.COUNTRY\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"34 ;\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"NOTE: Table WORK.QUERY_PRDSALE created, with 3 rows and 2 columns.\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"\",\n\t\t\"type\": \"normal\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"34 ! quit;\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"NOTE: PROCEDURE SQL used (Total process time):\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \" real time 0.00 seconds\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \" cpu time 0.00 seconds\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \" \",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"6 The SAS System Monday, August 21, 2023 02:56:00 PM\",\n\t\t\"type\": \"title\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"\",\n\t\t\"type\": \"title\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"35 ;run;quit;ods html5 close;\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t}\n]","mime":"application/vnd.sas.compute.log.lines"}]}]},{"kind":1,"language":"markdown","value":"A last comment in Markdown at the end of the document","outputs":[]},{"kind":1,"language":"markdown","value":"","outputs":[]}] \ No newline at end of file +[{"kind":1,"language":"markdown","value":"# Notebook to SAS Test","outputs":[]},{"kind":1,"language":"markdown","value":"## Python Code\n\nThis is some Python code","outputs":[]},{"kind":1,"language":"markdown","value":"This is a separate note in **Markdown** format.","outputs":[]},{"kind":2,"language":"python","value":"a, b = 4, 2\nprint(\"Result: \", a*10 + b)","outputs":[{"items":[{"data":"[\n\t{\n\t\t\"line\": \"44 ods html5;\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"NOTE: Writing HTML5 Body file: sashtml4.htm\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"45 proc python;\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"46 submit\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"NOTE: Resuming Python state from previous PROC PYTHON invocation.\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"46 ! ;\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"47 a, b = 4, 2\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"48 print(\\\"Result: \\\", a*10 + b)\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"49 endsubmit;\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"50 run;\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \">>>\",\n\t\t\"type\": \"normal\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"Result: 42\",\n\t\t\"type\": \"normal\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \">>> \",\n\t\t\"type\": \"normal\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"NOTE: PROCEDURE PYTHON used (Total process time):\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \" real time 0.00 seconds\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \" cpu time 0.00 seconds\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \" \",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"51 ;run;quit;ods html5 close;\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t}\n]","mime":"application/vnd.sas.compute.log.lines"}]}]},{"kind":1,"language":"markdown","value":"## R Code\n\nThis is some R code","outputs":[]},{"kind":1,"language":"markdown","value":"This is a separate note in **Markdown** format.","outputs":[]},{"kind":2,"language":"python","value":"die <- 1:6\npaste(\"Die Maths: \", die[3]*4 + die[6])","outputs":[{"items":[{"data":"[\n\t{\n\t\t\"line\": \"44 ods html5;\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"NOTE: Writing HTML5 Body file: sashtml4.htm\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"45 proc python;\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"46 submit\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"NOTE: Resuming Python state from previous PROC PYTHON invocation.\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"46 ! ;\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"47 a, b = 4, 2\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"48 print(\\\"Result: \\\", a*10 + b)\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"49 endsubmit;\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"50 run;\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \">>>\",\n\t\t\"type\": \"normal\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"Result: 42\",\n\t\t\"type\": \"normal\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \">>> \",\n\t\t\"type\": \"normal\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"NOTE: PROCEDURE PYTHON used (Total process time):\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \" real time 0.00 seconds\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \" cpu time 0.00 seconds\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \" \",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"51 ;run;quit;ods html5 close;\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t}\n]","mime":"application/vnd.sas.compute.log.lines"}]}]},{"kind":1,"language":"markdown","value":"## SAS Code","outputs":[]},{"kind":2,"language":"sas","value":"data work.prdsale;\n\tset sashelp.PRDSALE;\nrun;\n\nproc means data=work.prdsale;\nrun;","outputs":[{"items":[{"data":"\n\n\n\n\nSAS Output\n\n\n\n
\n
\n

The SAS System

\n
\n
\n

The MEANS Procedure

\n
\n
\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
VariableLabelNMeanStd DevMinimumMaximum
\n
\n
ACTUAL
\n
PREDICT
\n
QUARTER
\n
YEAR
\n
MONTH
\n
\n
\n
\n
Actual Sales
\n
Predicted Sales
\n
Quarter
\n
Year
\n
Month
\n
\n
\n
\n
1440
\n
1440
\n
1440
\n
1440
\n
1440
\n
\n
\n
\n
507.1784722
\n
490.4826389
\n
2.5000000
\n
1993.50
\n
12403.00
\n
\n
\n
\n
287.0313065
\n
285.7667904
\n
1.1184224
\n
0.5001737
\n
210.6291578
\n
\n
\n
\n
3.0000000
\n
0
\n
1.0000000
\n
1993.00
\n
12054.00
\n
\n
\n
\n
1000.00
\n
1000.00
\n
4.0000000
\n
1994.00
\n
12753.00
\n
\n
\n
\n
\n\n\n","mime":"application/vnd.sas.ods.html5"},{"data":"[\n\t{\n\t\t\"line\": \"16 ods html5;\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"NOTE: Writing HTML5 Body file: sashtml1.htm\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"17 data work.prdsale;\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"18 \\tset sashelp.PRDSALE;\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"19 run;\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"NOTE: There were 1440 observations read from the data set SASHELP.PRDSALE.\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"NOTE: The data set WORK.PRDSALE has 1440 observations and 10 variables.\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"NOTE: DATA statement used (Total process time):\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \" real time 0.00 seconds\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \" cpu time 0.01 seconds\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \" \",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"20 \",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"21 proc means data=work.prdsale;\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"22 run;\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"NOTE: There were 1440 observations read from the data set WORK.PRDSALE.\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"NOTE: The PROCEDURE MEANS printed page 1.\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"NOTE: PROCEDURE MEANS used (Total process time):\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \" real time 0.04 seconds\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \" cpu time 0.05 seconds\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \" \",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"23 ;run;quit;ods html5 close;\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t}\n]","mime":"application/vnd.sas.compute.log.lines"}]}]},{"kind":1,"language":"markdown","value":"## SQL Code","outputs":[]},{"kind":2,"language":"sql","value":"CREATE TABLE WORK.QUERY_PRDSALE AS\n SELECT\n (t1.COUNTRY) LABEL='Country' FORMAT=$CHAR10.,\n (SUM(t1.ACTUAL)) FORMAT=DOLLAR12.2 LENGTH=8 AS SUM_ACTUAL\n FROM\n WORK.PRDSALE t1\n GROUP BY\n t1.COUNTRY","outputs":[{"items":[{"data":"[\n\t{\n\t\t\"line\": \"24 ods html5;\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"NOTE: Writing HTML5 Body file: sashtml2.htm\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"25 proc sql;\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"26 CREATE TABLE WORK.QUERY_PRDSALE AS\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"27 SELECT\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"28 (t1.COUNTRY) LABEL='Country' FORMAT=$CHAR10.,\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"29 (SUM(t1.ACTUAL)) FORMAT=DOLLAR12.2 LENGTH=8 AS SUM_ACTUAL\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"30 FROM\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"31 WORK.PRDSALE t1\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"32 GROUP BY\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"33 t1.COUNTRY\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"34 ;\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"NOTE: Table WORK.QUERY_PRDSALE created, with 3 rows and 2 columns.\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"\",\n\t\t\"type\": \"normal\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"34 ! quit;\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"NOTE: PROCEDURE SQL used (Total process time):\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \" real time 0.00 seconds\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \" cpu time 0.00 seconds\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \" \",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"6 The SAS System Monday, August 21, 2023 02:56:00 PM\",\n\t\t\"type\": \"title\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"\",\n\t\t\"type\": \"title\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"35 ;run;quit;ods html5 close;\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t}\n]","mime":"application/vnd.sas.compute.log.lines"}]}]},{"kind":1,"language":"markdown","value":"A last comment in Markdown at the end of the document","outputs":[]},{"kind":1,"language":"markdown","value":"","outputs":[]}] \ No newline at end of file From 8f22394ddfabdb218ea92b54a7a36b329a2f7e27 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elijah=20C=C3=BAchulainn=20Reid?= <56865341+Wizzzzzzard@users.noreply.github.com> Date: Fri, 5 Dec 2025 09:53:55 +0000 Subject: [PATCH 04/34] fixing rlang testfixture file name --- server/test/embedded_lang/embedded_lang.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/test/embedded_lang/embedded_lang.test.ts b/server/test/embedded_lang/embedded_lang.test.ts index 9fcf14873..ce3613d74 100644 --- a/server/test/embedded_lang/embedded_lang.test.ts +++ b/server/test/embedded_lang/embedded_lang.test.ts @@ -66,7 +66,7 @@ describe("Test code zone for embedded language", () => { }); it("proc rlang", () => { - const doc = openDoc("server/testFixture/embedded_lang/proc_r.sas"); + const doc = openDoc("server/testFixture/embedded_lang/proc_rlang.sas"); const languageServer = new LanguageServiceProvider(doc); const codeZoneManager = languageServer.getCodeZoneManager(); From 97489e5ed4f029bfdc4fc023db9a73fb963865d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elijah=20C=C3=BAchulainn=20Reid?= <56865341+Wizzzzzzard@users.noreply.github.com> Date: Fri, 5 Dec 2025 09:58:24 +0000 Subject: [PATCH 05/34] adding a print statement to debug rlang zonelist --- server/test/embedded_lang/embedded_lang.test.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/server/test/embedded_lang/embedded_lang.test.ts b/server/test/embedded_lang/embedded_lang.test.ts index ce3613d74..c54dafe54 100644 --- a/server/test/embedded_lang/embedded_lang.test.ts +++ b/server/test/embedded_lang/embedded_lang.test.ts @@ -75,6 +75,8 @@ describe("Test code zone for embedded language", () => { zoneList.push(codeZoneManager.getCurrentZone(i, 1)); } + console.log(zoneList); + assert.equal(zoneList[1], CodeZoneManager.ZONE_TYPE.PROC_STMT); assert.equal(zoneList[2], CodeZoneManager.ZONE_TYPE.EMBEDDED_LANG); assert.equal(zoneList[4], CodeZoneManager.ZONE_TYPE.EMBEDDED_LANG); From 696bddd480c33105d4cfe873d0530cfd913f1902 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elijah=20C=C3=BAchulainn=20Reid?= <56865341+Wizzzzzzard@users.noreply.github.com> Date: Fri, 5 Dec 2025 12:13:35 +0000 Subject: [PATCH 06/34] updating debug function for zone_type assertions --- server/src/sas/CodeZoneManager.ts | 21 ++++++++------- .../test/embedded_lang/embedded_lang.test.ts | 27 ++++++++++++++----- .../testFixture/embedded_lang/proc_rlang.sas | 4 +-- 3 files changed, 33 insertions(+), 19 deletions(-) diff --git a/server/src/sas/CodeZoneManager.ts b/server/src/sas/CodeZoneManager.ts index 08ed4f0e5..cce17a0e8 100644 --- a/server/src/sas/CodeZoneManager.ts +++ b/server/src/sas/CodeZoneManager.ts @@ -2027,16 +2027,17 @@ export class CodeZoneManager { const zone = this._zone(stack, context); return zone.type; } - //only for debug - //function _getZoneName(zone) { - // for(var attr in ZONE_TYPE) { - // if (ZONE_TYPE.hasOwnProperty(attr)) { - // if (ZONE_TYPE[attr] === zone) { - // return attr; - // } - // } - // } - //} + + // Helper function for debugging zone types + static getZoneTypeName(zone: number): string { + for (const [attr, value] of Object.entries(CodeZoneManager.ZONE_TYPE)) { + if (value === zone) { + return attr; + } + } + return `UNKNOWN(${zone})`; + } + private _datasetOptions(context: Context) { let token1 = this._getNextEx(context), equal, diff --git a/server/test/embedded_lang/embedded_lang.test.ts b/server/test/embedded_lang/embedded_lang.test.ts index c54dafe54..e2b5cd24a 100644 --- a/server/test/embedded_lang/embedded_lang.test.ts +++ b/server/test/embedded_lang/embedded_lang.test.ts @@ -74,12 +74,25 @@ describe("Test code zone for embedded language", () => { for (let i = 0; i < doc.lineCount; i++) { zoneList.push(codeZoneManager.getCurrentZone(i, 1)); } - - console.log(zoneList); - - assert.equal(zoneList[1], CodeZoneManager.ZONE_TYPE.PROC_STMT); - assert.equal(zoneList[2], CodeZoneManager.ZONE_TYPE.EMBEDDED_LANG); - assert.equal(zoneList[4], CodeZoneManager.ZONE_TYPE.EMBEDDED_LANG); - assert.equal(zoneList[6], CodeZoneManager.ZONE_TYPE.PROC_STMT); + assert.equal( + zoneList[1], + CodeZoneManager.ZONE_TYPE.PROC_STMT, + `Expected PROC_STMT at line 2 but got ${CodeZoneManager.getZoneTypeName(zoneList[1])} (${zoneList[1]})`, + ); + assert.equal( + zoneList[2], + CodeZoneManager.ZONE_TYPE.EMBEDDED_LANG, + `Expected EMBEDDED_LANG at line 3 but got ${CodeZoneManager.getZoneTypeName(zoneList[2])} (${zoneList[2]})`, + ); + assert.equal( + zoneList[4], + CodeZoneManager.ZONE_TYPE.EMBEDDED_LANG, + `Expected EMBEDDED_LANG at line 5 but got ${CodeZoneManager.getZoneTypeName(zoneList[4])} (${zoneList[4]})`, + ); + assert.equal( + zoneList[6], + CodeZoneManager.ZONE_TYPE.PROC_STMT, + `Expected PROC_STMT at line 7 but got ${CodeZoneManager.getZoneTypeName(zoneList[6])} (${zoneList[6]})`, + ); }); }); diff --git a/server/testFixture/embedded_lang/proc_rlang.sas b/server/testFixture/embedded_lang/proc_rlang.sas index 29462e8b2..9fc829579 100644 --- a/server/testFixture/embedded_lang/proc_rlang.sas +++ b/server/testFixture/embedded_lang/proc_rlang.sas @@ -1,8 +1,8 @@ proc rlang; - submit; +submit; for (x in 1:6) { print(x) } print("first statement after for loop") - endsubmit; +endsubmit; run; From a05b28f43855ee5d8fbd2a10274c5cf440964936 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elijah=20C=C3=BAchulainn=20Reid?= <56865341+Wizzzzzzard@users.noreply.github.com> Date: Fri, 5 Dec 2025 12:16:52 +0000 Subject: [PATCH 07/34] =?UTF-8?q?DCO=20Remediation=20Commit=20for=20Elijah?= =?UTF-8?q?=20C=C3=BAchulainn=20Reid=20<56865341+Wizzzzzzard@users.noreply?= =?UTF-8?q?.github.com>?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit I, Elijah Cúchulainn Reid <56865341+Wizzzzzzard@users.noreply.github.com>, hereby add my Signed-off-by to this commit: 416922de21edd08151dcf41a720c58ff4359efc5 I, Elijah Cúchulainn Reid <56865341+Wizzzzzzard@users.noreply.github.com>, hereby add my Signed-off-by to this commit: 74a5d313b26664676679882063e72355c1b71bab I, Elijah Cúchulainn Reid <56865341+Wizzzzzzard@users.noreply.github.com>, hereby add my Signed-off-by to this commit: 0231587713a8e85e4aaad921cf10d8e03862835d I, Elijah Cúchulainn Reid <56865341+Wizzzzzzard@users.noreply.github.com>, hereby add my Signed-off-by to this commit: 8f22394ddfabdb218ea92b54a7a36b329a2f7e27 I, Elijah Cúchulainn Reid <56865341+Wizzzzzzard@users.noreply.github.com>, hereby add my Signed-off-by to this commit: 97489e5ed4f029bfdc4fc023db9a73fb963865d0 I, Elijah Cúchulainn Reid <56865341+Wizzzzzzard@users.noreply.github.com>, hereby add my Signed-off-by to this commit: 696bddd480c33105d4cfe873d0530cfd913f1902 Signed-off-by: Elijah Cúchulainn Reid <56865341+Wizzzzzzard@users.noreply.github.com> --- server/testFixture/embedded_lang/proc_rlang.sas | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/server/testFixture/embedded_lang/proc_rlang.sas b/server/testFixture/embedded_lang/proc_rlang.sas index 9fc829579..29462e8b2 100644 --- a/server/testFixture/embedded_lang/proc_rlang.sas +++ b/server/testFixture/embedded_lang/proc_rlang.sas @@ -1,8 +1,8 @@ proc rlang; -submit; + submit; for (x in 1:6) { print(x) } print("first statement after for loop") -endsubmit; + endsubmit; run; From 29dfd9a9c82a7183cad4da47c8194cb2b127b308 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elijah=20C=C3=BAchulainn=20Reid?= <56865341+Wizzzzzzard@users.noreply.github.com> Date: Fri, 5 Dec 2025 12:51:03 +0000 Subject: [PATCH 08/34] Updating Lexer to correctly parse PROC RLANG code --- server/src/sas/Lexer.ts | 72 +++++++++++++++++++ .../test/embedded_lang/embedded_lang.test.ts | 8 +-- 2 files changed, 76 insertions(+), 4 deletions(-) diff --git a/server/src/sas/Lexer.ts b/server/src/sas/Lexer.ts index a3f7ed182..c0797c991 100644 --- a/server/src/sas/Lexer.ts +++ b/server/src/sas/Lexer.ts @@ -86,6 +86,9 @@ enum EmbeddedLangState { PROC_LUA_DEF, PROC_LUA_SUBMIT_OR_INTERACTIVE, PROC_LUA_CODE, + PROC_RLANG_DEF, + PROC_RLANG_SUBMIT_OR_INTERACTIVE, + PROC_RLANG_CODE, } export class Lexer { start = { line: 0, column: 0 }; @@ -290,6 +293,8 @@ export class Lexer { ) { if (token.type === "text" && token.text === "PYTHON") { this.context.embeddedLangState = EmbeddedLangState.PROC_PYTHON_DEF; + } else if (token.type === "text" && token.text === "RLANG") { + this.context.embeddedLangState = EmbeddedLangState.PROC_RLANG_DEF; } else if (token.type === "text" && token.text === "LUA") { this.context.embeddedLangState = EmbeddedLangState.PROC_LUA_DEF; } @@ -324,6 +329,20 @@ export class Lexer { } break SWITCH; } + case EmbeddedLangState.PROC_RLANG_DEF: { + token = this._readToken(); + if (!token) { + break SWITCH; + } + if ( + token.type === "text" && + ["SUBMIT", "INTERACTIVE", "I"].includes(token.text) + ) { + this.context.embeddedLangState = + EmbeddedLangState.PROC_RLANG_SUBMIT_OR_INTERACTIVE; + } + break SWITCH; + } case EmbeddedLangState.PROC_PYTHON_SUBMIT_OR_INTERACTIVE: { token = this._readToken(); if (!token) { @@ -386,6 +405,59 @@ export class Lexer { token = this._foundEmbeddedCodeToken(this.curr); break SWITCH; } + case EmbeddedLangState.PROC_RLANG_SUBMIT_OR_INTERACTIVE: { + token = this._readToken(); + if (!token) { + break SWITCH; + } + if (token.type === "sep" && token.text === ";") { + this.context.embeddedLangState = EmbeddedLangState.PROC_RLANG_CODE; + } + break SWITCH; + } + case EmbeddedLangState.PROC_RLANG_CODE: { + // R doesn't have multi-line string delimiters like Python's triple quotes + for ( + let line = this.curr.line; + line < this.model.getLineCount(); + line++ + ) { + const lineContent = this._readEmbeddedCodeLine(this.curr, line); + let pos = 0; + let match; + do { + if (match) { + pos += match.index + match[0].length; + } + const stringReg = /("[^"]*?("|$))|('[^']*?('|$))/; + const commentReg = /#.*$/; + const secReg = + /(\b((endsubmit|endinteractive)(\s+|\/\*.*?\*\/)*;|(data|proc|%macro)\b[^'";]*;))/; + match = new RegExp( + `${stringReg.source}|${commentReg.source}|${secReg.source}`, + "m", + ).exec(lineContent.substring(pos)); + if (match) { + const matchedText = match[0]; + if ( + matchedText.startsWith("'") || + matchedText.startsWith('"') || + matchedText.startsWith("#") + ) { + // do nothing to skip string and single line comment + } else { + token = this._foundEmbeddedCodeToken(this.curr, { + line: line, + column: pos + match.index, + }); + break SWITCH; + } + } + } while (match); + } + token = this._foundEmbeddedCodeToken(this.curr); + break SWITCH; + } case EmbeddedLangState.PROC_LUA_SUBMIT_OR_INTERACTIVE: { token = this._readToken(); if (!token) { diff --git a/server/test/embedded_lang/embedded_lang.test.ts b/server/test/embedded_lang/embedded_lang.test.ts index e2b5cd24a..77a6abddb 100644 --- a/server/test/embedded_lang/embedded_lang.test.ts +++ b/server/test/embedded_lang/embedded_lang.test.ts @@ -77,22 +77,22 @@ describe("Test code zone for embedded language", () => { assert.equal( zoneList[1], CodeZoneManager.ZONE_TYPE.PROC_STMT, - `Expected PROC_STMT at line 2 but got ${CodeZoneManager.getZoneTypeName(zoneList[1])} (${zoneList[1]})`, + `Expected PROC_STMT at line 2 but got ${CodeZoneManager.getZoneTypeName(zoneList[1])} (Code: ${zoneList[1]})`, ); assert.equal( zoneList[2], CodeZoneManager.ZONE_TYPE.EMBEDDED_LANG, - `Expected EMBEDDED_LANG at line 3 but got ${CodeZoneManager.getZoneTypeName(zoneList[2])} (${zoneList[2]})`, + `Expected EMBEDDED_LANG at line 3 but got ${CodeZoneManager.getZoneTypeName(zoneList[2])} (Code: ${zoneList[2]})`, ); assert.equal( zoneList[4], CodeZoneManager.ZONE_TYPE.EMBEDDED_LANG, - `Expected EMBEDDED_LANG at line 5 but got ${CodeZoneManager.getZoneTypeName(zoneList[4])} (${zoneList[4]})`, + `Expected EMBEDDED_LANG at line 5 but got ${CodeZoneManager.getZoneTypeName(zoneList[4])} (Code: ${zoneList[4]})`, ); assert.equal( zoneList[6], CodeZoneManager.ZONE_TYPE.PROC_STMT, - `Expected PROC_STMT at line 7 but got ${CodeZoneManager.getZoneTypeName(zoneList[6])} (${zoneList[6]})`, + `Expected PROC_STMT at line 7 but got ${CodeZoneManager.getZoneTypeName(zoneList[6])} (Code: ${zoneList[6]})`, ); }); }); From 0e2b746c28a677f3c5c977e34e9b6619e50bb5c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elijah=20C=C3=BAchulainn=20Reid?= <56865341+Wizzzzzzard@users.noreply.github.com> Date: Fri, 5 Dec 2025 12:53:57 +0000 Subject: [PATCH 09/34] Adding PROC_RLANG to CodeZoneManager --- server/src/sas/CodeZoneManager.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/src/sas/CodeZoneManager.ts b/server/src/sas/CodeZoneManager.ts index cce17a0e8..6ba023c89 100644 --- a/server/src/sas/CodeZoneManager.ts +++ b/server/src/sas/CodeZoneManager.ts @@ -1132,7 +1132,7 @@ export class CodeZoneManager { this._getFullStmtName(context, this._procName, stmt); const zone = this._stmtEx(context, stmt); type = zone.type; - if (["PYTHON", "LUA"].includes(this._procName)) { + if (["PYTHON", "LUA", "RLANG"].includes(this._procName)) { if (!this._embeddedCodeStarted) { if (["SUBMIT", "INTERACTIVE", "I"].includes(stmt.text)) { this._embeddedCodeStarted = true; From 398c156d899f863a4255ba66bbc1e215fa3ba27e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elijah=20C=C3=BAchulainn=20Reid?= <56865341+Wizzzzzzard@users.noreply.github.com> Date: Fri, 5 Dec 2025 12:54:07 +0000 Subject: [PATCH 10/34] Adding R as an option to the parser for formatting --- server/src/sas/formatter/parser.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/server/src/sas/formatter/parser.ts b/server/src/sas/formatter/parser.ts index 07f4e6cba..6a2d35059 100644 --- a/server/src/sas/formatter/parser.ts +++ b/server/src/sas/formatter/parser.ts @@ -124,7 +124,7 @@ const preserveProcs = ( token: Token, model: Model, ) => { - // should not format python/lua, treat it as raw data + // should not format python/R/lua, treat it as raw data const lastStatement = region.children.length >= 2 && region.children[region.children.length - 1].children; @@ -135,7 +135,7 @@ const preserveProcs = ( region.children[0].children.length > 0 && lastStatement.length > 1 && "text" in region.children[0].children[1] && - /^(python|lua)$/i.test(region.children[0].children[1].text) && + /^(python|lua|rlang)$/i.test(region.children[0].children[1].text) && "text" in lastStatement[0] && /^(submit|interactive|i)$/i.test(lastStatement[0].text) ) { @@ -286,7 +286,7 @@ export const getParser = const node = tokens[i]; let parent = parents.length ? parents[parents.length - 1] : root; - //#region --- Preserve Python/Lua + //#region --- Preserve Python/R/Lua if (region && region.block) { preserveProc = preserveProcs(preserveProc, region, node, model); if (preserveProc === 0 && i === tokens.length - 1) { From ca52898965489f15594d3e2f45c46e19f214ad21 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elijah=20C=C3=BAchulainn=20Reid?= <56865341+Wizzzzzzard@users.noreply.github.com> Date: Fri, 5 Dec 2025 12:56:44 +0000 Subject: [PATCH 11/34] =?UTF-8?q?DCO=20Remediation=20Commit=20for=20Elijah?= =?UTF-8?q?=20C=C3=BAchulainn=20Reid=20<56865341+Wizzzzzzard@users.noreply?= =?UTF-8?q?.github.com>?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit I, Elijah Cúchulainn Reid <56865341+Wizzzzzzard@users.noreply.github.com>, hereby add my Signed-off-by to this commit: 29dfd9a9c82a7183cad4da47c8194cb2b127b308 I, Elijah Cúchulainn Reid <56865341+Wizzzzzzard@users.noreply.github.com>, hereby add my Signed-off-by to this commit: 0e2b746c28a677f3c5c977e34e9b6619e50bb5c8 I, Elijah Cúchulainn Reid <56865341+Wizzzzzzard@users.noreply.github.com>, hereby add my Signed-off-by to this commit: 398c156d899f863a4255ba66bbc1e215fa3ba27e Signed-off-by: Elijah Cúchulainn Reid <56865341+Wizzzzzzard@users.noreply.github.com> --- server/src/sas/LexerEx.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/server/src/sas/LexerEx.ts b/server/src/sas/LexerEx.ts index 8ca905437..848e766fe 100644 --- a/server/src/sas/LexerEx.ts +++ b/server/src/sas/LexerEx.ts @@ -3101,7 +3101,11 @@ export class LexerEx { this.setKeyword_(token, true); generalProcStmt = false; } - } else if (procName === "LUA" || procName === "PYTHON") { + } else if ( + procName === "LUA" || + procName === "PYTHON" || + procName === "RLANG" + ) { if (["SUBMIT", "INTERACTIVE", "I"].includes(word)) { const next = this.prefetch_({ pos: 1 }); if (next && next.text === ";" && next.type === "sep") { From 454a6abc0f0315e2148e6b844e65eec0b31e5726 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elijah=20C=C3=BAchulainn=20Reid?= <56865341+Wizzzzzzard@users.noreply.github.com> Date: Fri, 5 Dec 2025 13:22:10 +0000 Subject: [PATCH 12/34] test with simpler rlang script --- server/testFixture/embedded_lang/proc_rlang.sas | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/server/testFixture/embedded_lang/proc_rlang.sas b/server/testFixture/embedded_lang/proc_rlang.sas index 29462e8b2..9b9318eca 100644 --- a/server/testFixture/embedded_lang/proc_rlang.sas +++ b/server/testFixture/embedded_lang/proc_rlang.sas @@ -1,8 +1,6 @@ proc rlang; - submit; - for (x in 1:6) { - print(x) - } - print("first statement after for loop") - endsubmit; +submit; + x <- c(1, 2, 3) + print(x) +endsubmit; run; From 5e4da150377e10c622633f5025899c5b94724ee6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elijah=20C=C3=BAchulainn=20Reid?= <56865341+Wizzzzzzard@users.noreply.github.com> Date: Fri, 5 Dec 2025 13:28:23 +0000 Subject: [PATCH 13/34] resetting to more complex Rlang file MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Elijah Cúchulainn Reid <56865341+Wizzzzzzard@users.noreply.github.com> --- server/testFixture/embedded_lang/proc_rlang.sas | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/server/testFixture/embedded_lang/proc_rlang.sas b/server/testFixture/embedded_lang/proc_rlang.sas index 9b9318eca..ea6bd9ec0 100644 --- a/server/testFixture/embedded_lang/proc_rlang.sas +++ b/server/testFixture/embedded_lang/proc_rlang.sas @@ -1,6 +1,8 @@ proc rlang; -submit; - x <- c(1, 2, 3) - print(x) -endsubmit; + submit; + for (x in 1:6) { + print(x) + } + print("first statement after for loop"); + endsubmit; run; From a3be20b26470024842c20b5eda05ebd5ba1d4ecb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elijah=20C=C3=BAchulainn=20Reid?= <56865341+Wizzzzzzard@users.noreply.github.com> Date: Fri, 5 Dec 2025 13:36:02 +0000 Subject: [PATCH 14/34] fixing file MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Elijah Cúchulainn Reid <56865341+Wizzzzzzard@users.noreply.github.com> --- server/testFixture/embedded_lang/proc_rlang.sas | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/server/testFixture/embedded_lang/proc_rlang.sas b/server/testFixture/embedded_lang/proc_rlang.sas index ea6bd9ec0..da286d1e4 100644 --- a/server/testFixture/embedded_lang/proc_rlang.sas +++ b/server/testFixture/embedded_lang/proc_rlang.sas @@ -1,8 +1,8 @@ proc rlang; - submit; +submit; for (x in 1:6) { print(x) } - print("first statement after for loop"); - endsubmit; -run; + print("first statement after for loop") +endsubmit; +run; \ No newline at end of file From 412995f6155258c6464997b32dfa948aa7bcf394 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elijah=20C=C3=BAchulainn=20Reid?= <56865341+Wizzzzzzard@users.noreply.github.com> Date: Fri, 5 Dec 2025 13:42:33 +0000 Subject: [PATCH 15/34] adding more rlang tests to match pre-existing lua and python ones MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Elijah Cúchulainn Reid <56865341+Wizzzzzzard@users.noreply.github.com> --- .../components/util/SASCodeDocument.test.ts | 34 +++++++++++++++++++ client/testFixture/formatter/expected.sas | 21 ++++++++++++ client/testFixture/formatter/unformatted.sas | 20 +++++++++++ 3 files changed, 75 insertions(+) diff --git a/client/test/components/util/SASCodeDocument.test.ts b/client/test/components/util/SASCodeDocument.test.ts index e4cef507e..27017870c 100644 --- a/client/test/components/util/SASCodeDocument.test.ts +++ b/client/test/components/util/SASCodeDocument.test.ts @@ -38,6 +38,40 @@ run; assert.equal(sasCodeDoc.getWrappedCode(), expected); }); + it("wrap rlang code", () => { + const parameters: SASCodeDocumentParameters = { + languageId: "rlang", + code: `for (x in 1:6) { + print(x) +} +print("test")`, + selectedCode: "", + htmlStyle: "Illuminate", + outputHtml: true, + uuid: "519058ad-d33b-4b5c-9d23-4cc8d6ffb163", + checkKeyword: async () => false, + }; + + const sasCodeDoc = new SASCodeDocument(parameters); + + const expected = `/** LOG_START_INDICATOR **/ +title;footnote;ods _all_ close; +ods graphics on; +ods html5(id=vscode) style=Illuminate options(bitmap_mode='inline' svg_mode='inline') body="519058ad-d33b-4b5c-9d23-4cc8d6ffb163.htm"; +proc rlang; +submit; +for (x in 1:6) { + print(x) +} +print("test") +endsubmit; +run; +;*';*";*/;run;quit;ods html5(id=vscode) close; +`; + + assert.equal(sasCodeDoc.getWrappedCode(), expected); + }); + it("wrap sql code", () => { const parameters: SASCodeDocumentParameters = { languageId: "sql", diff --git a/client/testFixture/formatter/expected.sas b/client/testFixture/formatter/expected.sas index 7cb36d0c7..813c09385 100644 --- a/client/testFixture/formatter/expected.sas +++ b/client/testFixture/formatter/expected.sas @@ -46,6 +46,16 @@ def my_function(): endsubmit; run; +proc rlang; +submit; +# Reference to variable defined in previous PROC RLANG call +print(paste("x =", x)) +my_function <- function() { + print("Inside the proc step") +} +endsubmit; +run; + proc lua; submit; local dsid = sas.open("sashelp.company") -- open for input @@ -131,6 +141,17 @@ print('first statement after for loop') endinteractive; run; +proc rlang; +submit; +fruits <- c("apple", "banana", "cherry") +for (x in fruits) { + print(x) +} + +print('first statement after for loop') +endsubmit; +run; + proc lua; submit; diff --git a/client/testFixture/formatter/unformatted.sas b/client/testFixture/formatter/unformatted.sas index 923c7adaf..739699876 100644 --- a/client/testFixture/formatter/unformatted.sas +++ b/client/testFixture/formatter/unformatted.sas @@ -40,6 +40,15 @@ def my_function(): print("Inside the proc step") endsubmit; run; +proc rlang; +submit; +# Reference to variable defined in previous PROC RLANG call +print(paste("x =", x)) +my_function <- function() { + print("Inside the proc step") +} +endsubmit; +run; proc lua; submit; local dsid = sas.open("sashelp.company") -- open for input @@ -126,6 +135,17 @@ print('first statement after for loop') endinteractive; run; +proc rlang; +submit; +fruits <- c("apple", "banana", "cherry") +for (x in fruits) { + print(x) +} + +print('first statement after for loop') +endsubmit; +run; + proc lua; submit; From e62c8e80f46f1e9ba30708322a90fda6189d18f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elijah=20C=C3=BAchulainn=20Reid?= <56865341+Wizzzzzzard@users.noreply.github.com> Date: Fri, 5 Dec 2025 13:46:24 +0000 Subject: [PATCH 16/34] reverting embedded lang test assert for rlang MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit DCO Remediation Commit for Elijah Cúchulainn Reid <56865341+Wizzzzzzard@users.noreply.github.com> I, Elijah Cúchulainn Reid <56865341+Wizzzzzzard@users.noreply.github.com>, hereby add my Signed-off-by to this commit: 454a6abc0f0315e2148e6b844e65eec0b31e5726 Signed-off-by: Elijah Cúchulainn Reid <56865341+Wizzzzzzard@users.noreply.github.com> --- .../test/embedded_lang/embedded_lang.test.ts | 24 ++++--------------- 1 file changed, 4 insertions(+), 20 deletions(-) diff --git a/server/test/embedded_lang/embedded_lang.test.ts b/server/test/embedded_lang/embedded_lang.test.ts index 77a6abddb..4a0725e59 100644 --- a/server/test/embedded_lang/embedded_lang.test.ts +++ b/server/test/embedded_lang/embedded_lang.test.ts @@ -74,25 +74,9 @@ describe("Test code zone for embedded language", () => { for (let i = 0; i < doc.lineCount; i++) { zoneList.push(codeZoneManager.getCurrentZone(i, 1)); } - assert.equal( - zoneList[1], - CodeZoneManager.ZONE_TYPE.PROC_STMT, - `Expected PROC_STMT at line 2 but got ${CodeZoneManager.getZoneTypeName(zoneList[1])} (Code: ${zoneList[1]})`, - ); - assert.equal( - zoneList[2], - CodeZoneManager.ZONE_TYPE.EMBEDDED_LANG, - `Expected EMBEDDED_LANG at line 3 but got ${CodeZoneManager.getZoneTypeName(zoneList[2])} (Code: ${zoneList[2]})`, - ); - assert.equal( - zoneList[4], - CodeZoneManager.ZONE_TYPE.EMBEDDED_LANG, - `Expected EMBEDDED_LANG at line 5 but got ${CodeZoneManager.getZoneTypeName(zoneList[4])} (Code: ${zoneList[4]})`, - ); - assert.equal( - zoneList[6], - CodeZoneManager.ZONE_TYPE.PROC_STMT, - `Expected PROC_STMT at line 7 but got ${CodeZoneManager.getZoneTypeName(zoneList[6])} (Code: ${zoneList[6]})`, - ); + assert.equal(zoneList[1], CodeZoneManager.ZONE_TYPE.PROC_STMT); + assert.equal(zoneList[2], CodeZoneManager.ZONE_TYPE.EMBEDDED_LANG); + assert.equal(zoneList[4], CodeZoneManager.ZONE_TYPE.EMBEDDED_LANG); + assert.equal(zoneList[6], CodeZoneManager.ZONE_TYPE.PROC_STMT); }); }); From c5f8f7f726b54df1718f1f1bd6a4149ca9d9dba9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elijah=20C=C3=BAchulainn=20Reid?= <56865341+Wizzzzzzard@users.noreply.github.com> Date: Fri, 5 Dec 2025 13:57:35 +0000 Subject: [PATCH 17/34] updating references to "rlang" as a language to "r" to work better with VS Code's language server --- client/src/components/notebook/Controller.ts | 2 +- client/src/components/notebook/exporters/toHTML.ts | 2 +- client/src/components/notebook/exporters/toSAS.ts | 2 ++ client/src/components/utils/SASCodeDocument.ts | 2 +- client/test/components/util/SASCodeDocument.test.ts | 2 +- client/testFixture/sasnb_export.sasnb | 2 +- 6 files changed, 7 insertions(+), 5 deletions(-) diff --git a/client/src/components/notebook/Controller.ts b/client/src/components/notebook/Controller.ts index 01c1cfa1d..26a161609 100644 --- a/client/src/components/notebook/Controller.ts +++ b/client/src/components/notebook/Controller.ts @@ -11,7 +11,7 @@ export class NotebookController { readonly controllerId = "sas-notebook-controller-id"; readonly notebookType = "sas-notebook"; readonly label = "SAS Notebook"; - readonly supportedLanguages = ["sas", "sql", "python", "rlang"]; + readonly supportedLanguages = ["sas", "sql", "python", "r"]; private readonly _controller: vscode.NotebookController; private _executionOrder = 0; diff --git a/client/src/components/notebook/exporters/toHTML.ts b/client/src/components/notebook/exporters/toHTML.ts index beb79a704..917b85184 100644 --- a/client/src/components/notebook/exporters/toHTML.ts +++ b/client/src/components/notebook/exporters/toHTML.ts @@ -28,7 +28,7 @@ import { includeLogInNotebookExport } from "../../utils/settings"; const templatesDir = path.resolve(__dirname, "../notebook/exporters/templates"); hljs.registerLanguage("python", python); -hljs.registerLanguage("rlang", r); +hljs.registerLanguage("r", r); hljs.registerLanguage("sql", sql); export const exportToHTML = async ( diff --git a/client/src/components/notebook/exporters/toSAS.ts b/client/src/components/notebook/exporters/toSAS.ts index 3543a2b07..613050afc 100644 --- a/client/src/components/notebook/exporters/toSAS.ts +++ b/client/src/components/notebook/exporters/toSAS.ts @@ -15,6 +15,8 @@ const exportCell = (cell: NotebookCell) => { return text; case "python": return wrapPython(text); + case "r": + return wrapRlang(text); case "rlang": return wrapRlang(text); case "sql": diff --git a/client/src/components/utils/SASCodeDocument.ts b/client/src/components/utils/SASCodeDocument.ts index fba1e54a0..1425a485c 100644 --- a/client/src/components/utils/SASCodeDocument.ts +++ b/client/src/components/utils/SASCodeDocument.ts @@ -206,7 +206,7 @@ ${code}`; wrapped = this.wrapPython(wrapped); } - if (this.parameters.languageId === "rlang") { + if (this.parameters.languageId === "r" || this.parameters.languageId === "rlang") { wrapped = this.wrapRlang(wrapped); } diff --git a/client/test/components/util/SASCodeDocument.test.ts b/client/test/components/util/SASCodeDocument.test.ts index 27017870c..dbc4640c8 100644 --- a/client/test/components/util/SASCodeDocument.test.ts +++ b/client/test/components/util/SASCodeDocument.test.ts @@ -40,7 +40,7 @@ run; it("wrap rlang code", () => { const parameters: SASCodeDocumentParameters = { - languageId: "rlang", + languageId: "r", code: `for (x in 1:6) { print(x) } diff --git a/client/testFixture/sasnb_export.sasnb b/client/testFixture/sasnb_export.sasnb index f1167285b..55b2f338a 100644 --- a/client/testFixture/sasnb_export.sasnb +++ b/client/testFixture/sasnb_export.sasnb @@ -1 +1 @@ -[{"kind":1,"language":"markdown","value":"# Notebook to SAS Test","outputs":[]},{"kind":1,"language":"markdown","value":"## Python Code\n\nThis is some Python code","outputs":[]},{"kind":1,"language":"markdown","value":"This is a separate note in **Markdown** format.","outputs":[]},{"kind":2,"language":"python","value":"a, b = 4, 2\nprint(\"Result: \", a*10 + b)","outputs":[{"items":[{"data":"[\n\t{\n\t\t\"line\": \"44 ods html5;\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"NOTE: Writing HTML5 Body file: sashtml4.htm\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"45 proc python;\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"46 submit\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"NOTE: Resuming Python state from previous PROC PYTHON invocation.\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"46 ! ;\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"47 a, b = 4, 2\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"48 print(\\\"Result: \\\", a*10 + b)\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"49 endsubmit;\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"50 run;\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \">>>\",\n\t\t\"type\": \"normal\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"Result: 42\",\n\t\t\"type\": \"normal\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \">>> \",\n\t\t\"type\": \"normal\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"NOTE: PROCEDURE PYTHON used (Total process time):\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \" real time 0.00 seconds\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \" cpu time 0.00 seconds\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \" \",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"51 ;run;quit;ods html5 close;\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t}\n]","mime":"application/vnd.sas.compute.log.lines"}]}]},{"kind":1,"language":"markdown","value":"## R Code\n\nThis is some R code","outputs":[]},{"kind":1,"language":"markdown","value":"This is a separate note in **Markdown** format.","outputs":[]},{"kind":2,"language":"python","value":"die <- 1:6\npaste(\"Die Maths: \", die[3]*4 + die[6])","outputs":[{"items":[{"data":"[\n\t{\n\t\t\"line\": \"44 ods html5;\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"NOTE: Writing HTML5 Body file: sashtml4.htm\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"45 proc python;\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"46 submit\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"NOTE: Resuming Python state from previous PROC PYTHON invocation.\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"46 ! ;\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"47 a, b = 4, 2\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"48 print(\\\"Result: \\\", a*10 + b)\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"49 endsubmit;\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"50 run;\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \">>>\",\n\t\t\"type\": \"normal\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"Result: 42\",\n\t\t\"type\": \"normal\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \">>> \",\n\t\t\"type\": \"normal\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"NOTE: PROCEDURE PYTHON used (Total process time):\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \" real time 0.00 seconds\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \" cpu time 0.00 seconds\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \" \",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"51 ;run;quit;ods html5 close;\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t}\n]","mime":"application/vnd.sas.compute.log.lines"}]}]},{"kind":1,"language":"markdown","value":"## SAS Code","outputs":[]},{"kind":2,"language":"sas","value":"data work.prdsale;\n\tset sashelp.PRDSALE;\nrun;\n\nproc means data=work.prdsale;\nrun;","outputs":[{"items":[{"data":"\n\n\n\n\nSAS Output\n\n\n\n
\n
\n

The SAS System

\n
\n
\n

The MEANS Procedure

\n
\n
\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
VariableLabelNMeanStd DevMinimumMaximum
\n
\n
ACTUAL
\n
PREDICT
\n
QUARTER
\n
YEAR
\n
MONTH
\n
\n
\n
\n
Actual Sales
\n
Predicted Sales
\n
Quarter
\n
Year
\n
Month
\n
\n
\n
\n
1440
\n
1440
\n
1440
\n
1440
\n
1440
\n
\n
\n
\n
507.1784722
\n
490.4826389
\n
2.5000000
\n
1993.50
\n
12403.00
\n
\n
\n
\n
287.0313065
\n
285.7667904
\n
1.1184224
\n
0.5001737
\n
210.6291578
\n
\n
\n
\n
3.0000000
\n
0
\n
1.0000000
\n
1993.00
\n
12054.00
\n
\n
\n
\n
1000.00
\n
1000.00
\n
4.0000000
\n
1994.00
\n
12753.00
\n
\n
\n
\n
\n\n\n","mime":"application/vnd.sas.ods.html5"},{"data":"[\n\t{\n\t\t\"line\": \"16 ods html5;\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"NOTE: Writing HTML5 Body file: sashtml1.htm\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"17 data work.prdsale;\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"18 \\tset sashelp.PRDSALE;\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"19 run;\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"NOTE: There were 1440 observations read from the data set SASHELP.PRDSALE.\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"NOTE: The data set WORK.PRDSALE has 1440 observations and 10 variables.\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"NOTE: DATA statement used (Total process time):\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \" real time 0.00 seconds\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \" cpu time 0.01 seconds\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \" \",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"20 \",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"21 proc means data=work.prdsale;\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"22 run;\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"NOTE: There were 1440 observations read from the data set WORK.PRDSALE.\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"NOTE: The PROCEDURE MEANS printed page 1.\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"NOTE: PROCEDURE MEANS used (Total process time):\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \" real time 0.04 seconds\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \" cpu time 0.05 seconds\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \" \",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"23 ;run;quit;ods html5 close;\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t}\n]","mime":"application/vnd.sas.compute.log.lines"}]}]},{"kind":1,"language":"markdown","value":"## SQL Code","outputs":[]},{"kind":2,"language":"sql","value":"CREATE TABLE WORK.QUERY_PRDSALE AS\n SELECT\n (t1.COUNTRY) LABEL='Country' FORMAT=$CHAR10.,\n (SUM(t1.ACTUAL)) FORMAT=DOLLAR12.2 LENGTH=8 AS SUM_ACTUAL\n FROM\n WORK.PRDSALE t1\n GROUP BY\n t1.COUNTRY","outputs":[{"items":[{"data":"[\n\t{\n\t\t\"line\": \"24 ods html5;\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"NOTE: Writing HTML5 Body file: sashtml2.htm\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"25 proc sql;\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"26 CREATE TABLE WORK.QUERY_PRDSALE AS\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"27 SELECT\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"28 (t1.COUNTRY) LABEL='Country' FORMAT=$CHAR10.,\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"29 (SUM(t1.ACTUAL)) FORMAT=DOLLAR12.2 LENGTH=8 AS SUM_ACTUAL\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"30 FROM\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"31 WORK.PRDSALE t1\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"32 GROUP BY\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"33 t1.COUNTRY\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"34 ;\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"NOTE: Table WORK.QUERY_PRDSALE created, with 3 rows and 2 columns.\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"\",\n\t\t\"type\": \"normal\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"34 ! quit;\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"NOTE: PROCEDURE SQL used (Total process time):\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \" real time 0.00 seconds\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \" cpu time 0.00 seconds\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \" \",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"6 The SAS System Monday, August 21, 2023 02:56:00 PM\",\n\t\t\"type\": \"title\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"\",\n\t\t\"type\": \"title\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"35 ;run;quit;ods html5 close;\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t}\n]","mime":"application/vnd.sas.compute.log.lines"}]}]},{"kind":1,"language":"markdown","value":"A last comment in Markdown at the end of the document","outputs":[]},{"kind":1,"language":"markdown","value":"","outputs":[]}] \ No newline at end of file +[{"kind":1,"language":"markdown","value":"# Notebook to SAS Test","outputs":[]},{"kind":1,"language":"markdown","value":"## Python Code\n\nThis is some Python code","outputs":[]},{"kind":1,"language":"markdown","value":"This is a separate note in **Markdown** format.","outputs":[]},{"kind":2,"language":"python","value":"a, b = 4, 2\nprint(\"Result: \", a*10 + b)","outputs":[{"items":[{"data":"[\n\t{\n\t\t\"line\": \"44 ods html5;\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"NOTE: Writing HTML5 Body file: sashtml4.htm\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"45 proc python;\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"46 submit\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"NOTE: Resuming Python state from previous PROC PYTHON invocation.\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"46 ! ;\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"47 a, b = 4, 2\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"48 print(\\\"Result: \\\", a*10 + b)\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"49 endsubmit;\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"50 run;\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \">>>\",\n\t\t\"type\": \"normal\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"Result: 42\",\n\t\t\"type\": \"normal\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \">>> \",\n\t\t\"type\": \"normal\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"NOTE: PROCEDURE PYTHON used (Total process time):\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \" real time 0.00 seconds\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \" cpu time 0.00 seconds\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \" \",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"51 ;run;quit;ods html5 close;\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t}\n]","mime":"application/vnd.sas.compute.log.lines"}]}]},{"kind":1,"language":"markdown","value":"## R Code\n\nThis is some R code","outputs":[]},{"kind":1,"language":"markdown","value":"This is a separate note in **Markdown** format.","outputs":[]},{"kind":2,"language":"rlang","value":"die <- 1:6\npaste(\"Die Maths: \", die[3]*4 + die[6])","outputs":[{"items":[{"data":"[\n\t{\n\t\t\"line\": \"44 ods html5;\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"NOTE: Writing HTML5 Body file: sashtml4.htm\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"45 proc python;\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"46 submit\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"NOTE: Resuming Python state from previous PROC PYTHON invocation.\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"46 ! ;\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"47 a, b = 4, 2\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"48 print(\\\"Result: \\\", a*10 + b)\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"49 endsubmit;\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"50 run;\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \">>>\",\n\t\t\"type\": \"normal\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"Result: 42\",\n\t\t\"type\": \"normal\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \">>> \",\n\t\t\"type\": \"normal\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"NOTE: PROCEDURE PYTHON used (Total process time):\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \" real time 0.00 seconds\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \" cpu time 0.00 seconds\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \" \",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"51 ;run;quit;ods html5 close;\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t}\n]","mime":"application/vnd.sas.compute.log.lines"}]}]},{"kind":1,"language":"markdown","value":"## SAS Code","outputs":[]},{"kind":2,"language":"sas","value":"data work.prdsale;\n\tset sashelp.PRDSALE;\nrun;\n\nproc means data=work.prdsale;\nrun;","outputs":[{"items":[{"data":"\n\n\n\n\nSAS Output\n\n\n\n
\n
\n

The SAS System

\n
\n
\n

The MEANS Procedure

\n
\n
\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
VariableLabelNMeanStd DevMinimumMaximum
\n
\n
ACTUAL
\n
PREDICT
\n
QUARTER
\n
YEAR
\n
MONTH
\n
\n
\n
\n
Actual Sales
\n
Predicted Sales
\n
Quarter
\n
Year
\n
Month
\n
\n
\n
\n
1440
\n
1440
\n
1440
\n
1440
\n
1440
\n
\n
\n
\n
507.1784722
\n
490.4826389
\n
2.5000000
\n
1993.50
\n
12403.00
\n
\n
\n
\n
287.0313065
\n
285.7667904
\n
1.1184224
\n
0.5001737
\n
210.6291578
\n
\n
\n
\n
3.0000000
\n
0
\n
1.0000000
\n
1993.00
\n
12054.00
\n
\n
\n
\n
1000.00
\n
1000.00
\n
4.0000000
\n
1994.00
\n
12753.00
\n
\n
\n
\n
\n\n\n","mime":"application/vnd.sas.ods.html5"},{"data":"[\n\t{\n\t\t\"line\": \"16 ods html5;\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"NOTE: Writing HTML5 Body file: sashtml1.htm\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"17 data work.prdsale;\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"18 \\tset sashelp.PRDSALE;\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"19 run;\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"NOTE: There were 1440 observations read from the data set SASHELP.PRDSALE.\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"NOTE: The data set WORK.PRDSALE has 1440 observations and 10 variables.\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"NOTE: DATA statement used (Total process time):\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \" real time 0.00 seconds\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \" cpu time 0.01 seconds\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \" \",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"20 \",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"21 proc means data=work.prdsale;\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"22 run;\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"NOTE: There were 1440 observations read from the data set WORK.PRDSALE.\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"NOTE: The PROCEDURE MEANS printed page 1.\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"NOTE: PROCEDURE MEANS used (Total process time):\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \" real time 0.04 seconds\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \" cpu time 0.05 seconds\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \" \",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"23 ;run;quit;ods html5 close;\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t}\n]","mime":"application/vnd.sas.compute.log.lines"}]}]},{"kind":1,"language":"markdown","value":"## SQL Code","outputs":[]},{"kind":2,"language":"sql","value":"CREATE TABLE WORK.QUERY_PRDSALE AS\n SELECT\n (t1.COUNTRY) LABEL='Country' FORMAT=$CHAR10.,\n (SUM(t1.ACTUAL)) FORMAT=DOLLAR12.2 LENGTH=8 AS SUM_ACTUAL\n FROM\n WORK.PRDSALE t1\n GROUP BY\n t1.COUNTRY","outputs":[{"items":[{"data":"[\n\t{\n\t\t\"line\": \"24 ods html5;\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"NOTE: Writing HTML5 Body file: sashtml2.htm\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"25 proc sql;\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"26 CREATE TABLE WORK.QUERY_PRDSALE AS\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"27 SELECT\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"28 (t1.COUNTRY) LABEL='Country' FORMAT=$CHAR10.,\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"29 (SUM(t1.ACTUAL)) FORMAT=DOLLAR12.2 LENGTH=8 AS SUM_ACTUAL\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"30 FROM\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"31 WORK.PRDSALE t1\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"32 GROUP BY\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"33 t1.COUNTRY\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"34 ;\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"NOTE: Table WORK.QUERY_PRDSALE created, with 3 rows and 2 columns.\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"\",\n\t\t\"type\": \"normal\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"34 ! quit;\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"NOTE: PROCEDURE SQL used (Total process time):\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \" real time 0.00 seconds\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \" cpu time 0.00 seconds\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \" \",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"6 The SAS System Monday, August 21, 2023 02:56:00 PM\",\n\t\t\"type\": \"title\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"\",\n\t\t\"type\": \"title\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"35 ;run;quit;ods html5 close;\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t}\n]","mime":"application/vnd.sas.compute.log.lines"}]}]},{"kind":1,"language":"markdown","value":"A last comment in Markdown at the end of the document","outputs":[]},{"kind":1,"language":"markdown","value":"","outputs":[]}] \ No newline at end of file From d4da7ade4c221ba091ab0be3182c6b8a9d1dd86c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elijah=20C=C3=BAchulainn=20Reid?= <56865341+Wizzzzzzard@users.noreply.github.com> Date: Fri, 5 Dec 2025 13:58:27 +0000 Subject: [PATCH 18/34] making spacing a bit nicer MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit DCO Remediation Commit for Elijah Cúchulainn Reid <56865341+Wizzzzzzard@users.noreply.github.com> I, Elijah Cúchulainn Reid <56865341+Wizzzzzzard@users.noreply.github.com>, hereby add my Signed-off-by to this commit: c5f8f7f726b54df1718f1f1bd6a4149ca9d9dba9 Signed-off-by: Elijah Cúchulainn Reid <56865341+Wizzzzzzard@users.noreply.github.com> --- client/src/components/utils/SASCodeDocument.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/client/src/components/utils/SASCodeDocument.ts b/client/src/components/utils/SASCodeDocument.ts index 1425a485c..c15cedf23 100644 --- a/client/src/components/utils/SASCodeDocument.ts +++ b/client/src/components/utils/SASCodeDocument.ts @@ -206,7 +206,10 @@ ${code}`; wrapped = this.wrapPython(wrapped); } - if (this.parameters.languageId === "r" || this.parameters.languageId === "rlang") { + if ( + this.parameters.languageId === "r" || + this.parameters.languageId === "rlang" + ) { wrapped = this.wrapRlang(wrapped); } From 7e58b1ca2c78c5ff68b8d6f8e4db19875b877e31 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elijah=20C=C3=BAchulainn=20Reid?= <56865341+Wizzzzzzard@users.noreply.github.com> Date: Fri, 5 Dec 2025 14:06:07 +0000 Subject: [PATCH 19/34] tweaking R UID to see if it makes a difference MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Elijah Cúchulainn Reid <56865341+Wizzzzzzard@users.noreply.github.com> --- client/src/components/ContentNavigator/convert.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/src/components/ContentNavigator/convert.ts b/client/src/components/ContentNavigator/convert.ts index 80a03568e..c64619fc6 100644 --- a/client/src/components/ContentNavigator/convert.ts +++ b/client/src/components/ContentNavigator/convert.ts @@ -20,7 +20,7 @@ const stepRef: Record = { sas: "a7190700-f59c-4a94-afe2-214ce639fcde", sql: "a7190700-f59c-4a94-afe2-214ce639fcde", python: "ab59f8c4-af9a-4608-a5d5-a8365357bb99", - rlang: "ab59f8c4-af9a-4608-a5d5-a8365357bb99", + rlang: "ab59f8c4-af9a-4608-a5d6-a8365357bb98", }; const stepTitle: Record = { From 925ff772fee28dcffc74a44512a8c325b22b26f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elijah=20C=C3=BAchulainn=20Reid?= <56865341+Wizzzzzzard@users.noreply.github.com> Date: Fri, 5 Dec 2025 14:27:58 +0000 Subject: [PATCH 20/34] Updating notebook with correctly highlighted R code MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Elijah Cúchulainn Reid <56865341+Wizzzzzzard@users.noreply.github.com> --- client/testFixture/sasnb_export.sasnb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/testFixture/sasnb_export.sasnb b/client/testFixture/sasnb_export.sasnb index 55b2f338a..ab5ac4a37 100644 --- a/client/testFixture/sasnb_export.sasnb +++ b/client/testFixture/sasnb_export.sasnb @@ -1 +1 @@ -[{"kind":1,"language":"markdown","value":"# Notebook to SAS Test","outputs":[]},{"kind":1,"language":"markdown","value":"## Python Code\n\nThis is some Python code","outputs":[]},{"kind":1,"language":"markdown","value":"This is a separate note in **Markdown** format.","outputs":[]},{"kind":2,"language":"python","value":"a, b = 4, 2\nprint(\"Result: \", a*10 + b)","outputs":[{"items":[{"data":"[\n\t{\n\t\t\"line\": \"44 ods html5;\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"NOTE: Writing HTML5 Body file: sashtml4.htm\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"45 proc python;\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"46 submit\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"NOTE: Resuming Python state from previous PROC PYTHON invocation.\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"46 ! ;\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"47 a, b = 4, 2\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"48 print(\\\"Result: \\\", a*10 + b)\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"49 endsubmit;\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"50 run;\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \">>>\",\n\t\t\"type\": \"normal\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"Result: 42\",\n\t\t\"type\": \"normal\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \">>> \",\n\t\t\"type\": \"normal\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"NOTE: PROCEDURE PYTHON used (Total process time):\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \" real time 0.00 seconds\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \" cpu time 0.00 seconds\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \" \",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"51 ;run;quit;ods html5 close;\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t}\n]","mime":"application/vnd.sas.compute.log.lines"}]}]},{"kind":1,"language":"markdown","value":"## R Code\n\nThis is some R code","outputs":[]},{"kind":1,"language":"markdown","value":"This is a separate note in **Markdown** format.","outputs":[]},{"kind":2,"language":"rlang","value":"die <- 1:6\npaste(\"Die Maths: \", die[3]*4 + die[6])","outputs":[{"items":[{"data":"[\n\t{\n\t\t\"line\": \"44 ods html5;\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"NOTE: Writing HTML5 Body file: sashtml4.htm\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"45 proc python;\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"46 submit\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"NOTE: Resuming Python state from previous PROC PYTHON invocation.\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"46 ! ;\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"47 a, b = 4, 2\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"48 print(\\\"Result: \\\", a*10 + b)\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"49 endsubmit;\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"50 run;\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \">>>\",\n\t\t\"type\": \"normal\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"Result: 42\",\n\t\t\"type\": \"normal\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \">>> \",\n\t\t\"type\": \"normal\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"NOTE: PROCEDURE PYTHON used (Total process time):\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \" real time 0.00 seconds\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \" cpu time 0.00 seconds\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \" \",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"51 ;run;quit;ods html5 close;\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t}\n]","mime":"application/vnd.sas.compute.log.lines"}]}]},{"kind":1,"language":"markdown","value":"## SAS Code","outputs":[]},{"kind":2,"language":"sas","value":"data work.prdsale;\n\tset sashelp.PRDSALE;\nrun;\n\nproc means data=work.prdsale;\nrun;","outputs":[{"items":[{"data":"\n\n\n\n\nSAS Output\n\n\n\n
\n
\n

The SAS System

\n
\n
\n

The MEANS Procedure

\n
\n
\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
VariableLabelNMeanStd DevMinimumMaximum
\n
\n
ACTUAL
\n
PREDICT
\n
QUARTER
\n
YEAR
\n
MONTH
\n
\n
\n
\n
Actual Sales
\n
Predicted Sales
\n
Quarter
\n
Year
\n
Month
\n
\n
\n
\n
1440
\n
1440
\n
1440
\n
1440
\n
1440
\n
\n
\n
\n
507.1784722
\n
490.4826389
\n
2.5000000
\n
1993.50
\n
12403.00
\n
\n
\n
\n
287.0313065
\n
285.7667904
\n
1.1184224
\n
0.5001737
\n
210.6291578
\n
\n
\n
\n
3.0000000
\n
0
\n
1.0000000
\n
1993.00
\n
12054.00
\n
\n
\n
\n
1000.00
\n
1000.00
\n
4.0000000
\n
1994.00
\n
12753.00
\n
\n
\n
\n
\n\n\n","mime":"application/vnd.sas.ods.html5"},{"data":"[\n\t{\n\t\t\"line\": \"16 ods html5;\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"NOTE: Writing HTML5 Body file: sashtml1.htm\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"17 data work.prdsale;\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"18 \\tset sashelp.PRDSALE;\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"19 run;\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"NOTE: There were 1440 observations read from the data set SASHELP.PRDSALE.\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"NOTE: The data set WORK.PRDSALE has 1440 observations and 10 variables.\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"NOTE: DATA statement used (Total process time):\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \" real time 0.00 seconds\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \" cpu time 0.01 seconds\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \" \",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"20 \",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"21 proc means data=work.prdsale;\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"22 run;\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"NOTE: There were 1440 observations read from the data set WORK.PRDSALE.\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"NOTE: The PROCEDURE MEANS printed page 1.\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"NOTE: PROCEDURE MEANS used (Total process time):\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \" real time 0.04 seconds\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \" cpu time 0.05 seconds\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \" \",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"23 ;run;quit;ods html5 close;\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t}\n]","mime":"application/vnd.sas.compute.log.lines"}]}]},{"kind":1,"language":"markdown","value":"## SQL Code","outputs":[]},{"kind":2,"language":"sql","value":"CREATE TABLE WORK.QUERY_PRDSALE AS\n SELECT\n (t1.COUNTRY) LABEL='Country' FORMAT=$CHAR10.,\n (SUM(t1.ACTUAL)) FORMAT=DOLLAR12.2 LENGTH=8 AS SUM_ACTUAL\n FROM\n WORK.PRDSALE t1\n GROUP BY\n t1.COUNTRY","outputs":[{"items":[{"data":"[\n\t{\n\t\t\"line\": \"24 ods html5;\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"NOTE: Writing HTML5 Body file: sashtml2.htm\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"25 proc sql;\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"26 CREATE TABLE WORK.QUERY_PRDSALE AS\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"27 SELECT\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"28 (t1.COUNTRY) LABEL='Country' FORMAT=$CHAR10.,\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"29 (SUM(t1.ACTUAL)) FORMAT=DOLLAR12.2 LENGTH=8 AS SUM_ACTUAL\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"30 FROM\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"31 WORK.PRDSALE t1\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"32 GROUP BY\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"33 t1.COUNTRY\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"34 ;\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"NOTE: Table WORK.QUERY_PRDSALE created, with 3 rows and 2 columns.\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"\",\n\t\t\"type\": \"normal\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"34 ! quit;\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"NOTE: PROCEDURE SQL used (Total process time):\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \" real time 0.00 seconds\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \" cpu time 0.00 seconds\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \" \",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"6 The SAS System Monday, August 21, 2023 02:56:00 PM\",\n\t\t\"type\": \"title\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"\",\n\t\t\"type\": \"title\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"35 ;run;quit;ods html5 close;\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t}\n]","mime":"application/vnd.sas.compute.log.lines"}]}]},{"kind":1,"language":"markdown","value":"A last comment in Markdown at the end of the document","outputs":[]},{"kind":1,"language":"markdown","value":"","outputs":[]}] \ No newline at end of file +[{"kind":1,"language":"markdown","value":"# Notebook to SAS Test","outputs":[]},{"kind":1,"language":"markdown","value":"## Python Code\n\nThis is some Python code","outputs":[]},{"kind":1,"language":"markdown","value":"This is a separate note in **Markdown** format.","outputs":[]},{"kind":2,"language":"python","value":"a, b = 4, 2\nprint(\"Result: \", a*10 + b)","outputs":[{"items":[{"data":"[\n\t{\n\t\t\"line\": \"44 ods html5;\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"NOTE: Writing HTML5 Body file: sashtml4.htm\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"45 proc python;\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"46 submit\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"NOTE: Resuming Python state from previous PROC PYTHON invocation.\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"46 ! ;\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"47 a, b = 4, 2\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"48 print(\\\"Result: \\\", a*10 + b)\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"49 endsubmit;\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"50 run;\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \">>>\",\n\t\t\"type\": \"normal\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"Result: 42\",\n\t\t\"type\": \"normal\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \">>> \",\n\t\t\"type\": \"normal\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"NOTE: PROCEDURE PYTHON used (Total process time):\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \" real time 0.00 seconds\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \" cpu time 0.00 seconds\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \" \",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"51 ;run;quit;ods html5 close;\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t}\n]","mime":"application/vnd.sas.compute.log.lines"}]}]},{"kind":1,"language":"markdown","value":"## R Code\n\nThis is some R code","outputs":[]},{"kind":1,"language":"markdown","value":"This is a separate note in **Markdown** format.","outputs":[]},{"kind":2,"language":"r","value":"die <- 1:6\npaste(\"Die Maths: \", die[3]*4 + die[6])","outputs":[{"items":[{"data":"[\n\t{\n\t\t\"line\": \"44 ods html5;\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"NOTE: Writing HTML5 Body file: sashtml4.htm\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"45 proc python;\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"46 submit\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"NOTE: Resuming Python state from previous PROC PYTHON invocation.\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"46 ! ;\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"47 a, b = 4, 2\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"48 print(\\\"Result: \\\", a*10 + b)\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"49 endsubmit;\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"50 run;\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \">>>\",\n\t\t\"type\": \"normal\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"Result: 42\",\n\t\t\"type\": \"normal\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \">>> \",\n\t\t\"type\": \"normal\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"NOTE: PROCEDURE PYTHON used (Total process time):\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \" real time 0.00 seconds\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \" cpu time 0.00 seconds\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \" \",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"51 ;run;quit;ods html5 close;\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t}\n]","mime":"application/vnd.sas.compute.log.lines"}]}]},{"kind":1,"language":"markdown","value":"## SAS Code","outputs":[]},{"kind":2,"language":"sas","value":"data work.prdsale;\n\tset sashelp.PRDSALE;\nrun;\n\nproc means data=work.prdsale;\nrun;","outputs":[{"items":[{"data":"\n\n\n\n\nSAS Output\n\n\n\n
\n
\n

The SAS System

\n
\n
\n

The MEANS Procedure

\n
\n
\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
VariableLabelNMeanStd DevMinimumMaximum
\n
\n
ACTUAL
\n
PREDICT
\n
QUARTER
\n
YEAR
\n
MONTH
\n
\n
\n
\n
Actual Sales
\n
Predicted Sales
\n
Quarter
\n
Year
\n
Month
\n
\n
\n
\n
1440
\n
1440
\n
1440
\n
1440
\n
1440
\n
\n
\n
\n
507.1784722
\n
490.4826389
\n
2.5000000
\n
1993.50
\n
12403.00
\n
\n
\n
\n
287.0313065
\n
285.7667904
\n
1.1184224
\n
0.5001737
\n
210.6291578
\n
\n
\n
\n
3.0000000
\n
0
\n
1.0000000
\n
1993.00
\n
12054.00
\n
\n
\n
\n
1000.00
\n
1000.00
\n
4.0000000
\n
1994.00
\n
12753.00
\n
\n
\n
\n
\n\n\n","mime":"application/vnd.sas.ods.html5"},{"data":"[\n\t{\n\t\t\"line\": \"16 ods html5;\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"NOTE: Writing HTML5 Body file: sashtml1.htm\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"17 data work.prdsale;\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"18 \\tset sashelp.PRDSALE;\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"19 run;\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"NOTE: There were 1440 observations read from the data set SASHELP.PRDSALE.\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"NOTE: The data set WORK.PRDSALE has 1440 observations and 10 variables.\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"NOTE: DATA statement used (Total process time):\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \" real time 0.00 seconds\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \" cpu time 0.01 seconds\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \" \",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"20 \",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"21 proc means data=work.prdsale;\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"22 run;\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"NOTE: There were 1440 observations read from the data set WORK.PRDSALE.\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"NOTE: The PROCEDURE MEANS printed page 1.\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"NOTE: PROCEDURE MEANS used (Total process time):\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \" real time 0.04 seconds\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \" cpu time 0.05 seconds\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \" \",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"23 ;run;quit;ods html5 close;\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t}\n]","mime":"application/vnd.sas.compute.log.lines"}]}]},{"kind":1,"language":"markdown","value":"## SQL Code","outputs":[]},{"kind":2,"language":"sql","value":"CREATE TABLE WORK.QUERY_PRDSALE AS\n SELECT\n (t1.COUNTRY) LABEL='Country' FORMAT=$CHAR10.,\n (SUM(t1.ACTUAL)) FORMAT=DOLLAR12.2 LENGTH=8 AS SUM_ACTUAL\n FROM\n WORK.PRDSALE t1\n GROUP BY\n t1.COUNTRY","outputs":[{"items":[{"data":"[\n\t{\n\t\t\"line\": \"24 ods html5;\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"NOTE: Writing HTML5 Body file: sashtml2.htm\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"25 proc sql;\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"26 CREATE TABLE WORK.QUERY_PRDSALE AS\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"27 SELECT\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"28 (t1.COUNTRY) LABEL='Country' FORMAT=$CHAR10.,\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"29 (SUM(t1.ACTUAL)) FORMAT=DOLLAR12.2 LENGTH=8 AS SUM_ACTUAL\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"30 FROM\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"31 WORK.PRDSALE t1\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"32 GROUP BY\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"33 t1.COUNTRY\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"34 ;\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"NOTE: Table WORK.QUERY_PRDSALE created, with 3 rows and 2 columns.\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"\",\n\t\t\"type\": \"normal\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"34 ! quit;\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"NOTE: PROCEDURE SQL used (Total process time):\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \" real time 0.00 seconds\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \" cpu time 0.00 seconds\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \" \",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"6 The SAS System Monday, August 21, 2023 02:56:00 PM\",\n\t\t\"type\": \"title\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"\",\n\t\t\"type\": \"title\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"35 ;run;quit;ods html5 close;\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t}\n]","mime":"application/vnd.sas.compute.log.lines"}]}]},{"kind":1,"language":"markdown","value":"A last comment in Markdown at the end of the document","outputs":[]},{"kind":1,"language":"markdown","value":"","outputs":[]}] \ No newline at end of file From 9e1b66a06381a029dcb49a0e8fd8c085bcab802a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elijah=20C=C3=BAchulainn=20Reid?= <56865341+Wizzzzzzard@users.noreply.github.com> Date: Fri, 5 Dec 2025 14:40:58 +0000 Subject: [PATCH 21/34] uploading notebook with R output MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Elijah Cúchulainn Reid <56865341+Wizzzzzzard@users.noreply.github.com> --- client/testFixture/sasnb_export.sasnb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/testFixture/sasnb_export.sasnb b/client/testFixture/sasnb_export.sasnb index ab5ac4a37..6f1183941 100644 --- a/client/testFixture/sasnb_export.sasnb +++ b/client/testFixture/sasnb_export.sasnb @@ -1 +1 @@ -[{"kind":1,"language":"markdown","value":"# Notebook to SAS Test","outputs":[]},{"kind":1,"language":"markdown","value":"## Python Code\n\nThis is some Python code","outputs":[]},{"kind":1,"language":"markdown","value":"This is a separate note in **Markdown** format.","outputs":[]},{"kind":2,"language":"python","value":"a, b = 4, 2\nprint(\"Result: \", a*10 + b)","outputs":[{"items":[{"data":"[\n\t{\n\t\t\"line\": \"44 ods html5;\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"NOTE: Writing HTML5 Body file: sashtml4.htm\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"45 proc python;\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"46 submit\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"NOTE: Resuming Python state from previous PROC PYTHON invocation.\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"46 ! ;\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"47 a, b = 4, 2\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"48 print(\\\"Result: \\\", a*10 + b)\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"49 endsubmit;\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"50 run;\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \">>>\",\n\t\t\"type\": \"normal\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"Result: 42\",\n\t\t\"type\": \"normal\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \">>> \",\n\t\t\"type\": \"normal\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"NOTE: PROCEDURE PYTHON used (Total process time):\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \" real time 0.00 seconds\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \" cpu time 0.00 seconds\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \" \",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"51 ;run;quit;ods html5 close;\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t}\n]","mime":"application/vnd.sas.compute.log.lines"}]}]},{"kind":1,"language":"markdown","value":"## R Code\n\nThis is some R code","outputs":[]},{"kind":1,"language":"markdown","value":"This is a separate note in **Markdown** format.","outputs":[]},{"kind":2,"language":"r","value":"die <- 1:6\npaste(\"Die Maths: \", die[3]*4 + die[6])","outputs":[{"items":[{"data":"[\n\t{\n\t\t\"line\": \"44 ods html5;\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"NOTE: Writing HTML5 Body file: sashtml4.htm\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"45 proc python;\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"46 submit\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"NOTE: Resuming Python state from previous PROC PYTHON invocation.\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"46 ! ;\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"47 a, b = 4, 2\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"48 print(\\\"Result: \\\", a*10 + b)\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"49 endsubmit;\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"50 run;\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \">>>\",\n\t\t\"type\": \"normal\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"Result: 42\",\n\t\t\"type\": \"normal\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \">>> \",\n\t\t\"type\": \"normal\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"NOTE: PROCEDURE PYTHON used (Total process time):\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \" real time 0.00 seconds\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \" cpu time 0.00 seconds\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \" \",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"51 ;run;quit;ods html5 close;\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t}\n]","mime":"application/vnd.sas.compute.log.lines"}]}]},{"kind":1,"language":"markdown","value":"## SAS Code","outputs":[]},{"kind":2,"language":"sas","value":"data work.prdsale;\n\tset sashelp.PRDSALE;\nrun;\n\nproc means data=work.prdsale;\nrun;","outputs":[{"items":[{"data":"\n\n\n\n\nSAS Output\n\n\n\n
\n
\n

The SAS System

\n
\n
\n

The MEANS Procedure

\n
\n
\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
VariableLabelNMeanStd DevMinimumMaximum
\n
\n
ACTUAL
\n
PREDICT
\n
QUARTER
\n
YEAR
\n
MONTH
\n
\n
\n
\n
Actual Sales
\n
Predicted Sales
\n
Quarter
\n
Year
\n
Month
\n
\n
\n
\n
1440
\n
1440
\n
1440
\n
1440
\n
1440
\n
\n
\n
\n
507.1784722
\n
490.4826389
\n
2.5000000
\n
1993.50
\n
12403.00
\n
\n
\n
\n
287.0313065
\n
285.7667904
\n
1.1184224
\n
0.5001737
\n
210.6291578
\n
\n
\n
\n
3.0000000
\n
0
\n
1.0000000
\n
1993.00
\n
12054.00
\n
\n
\n
\n
1000.00
\n
1000.00
\n
4.0000000
\n
1994.00
\n
12753.00
\n
\n
\n
\n
\n\n\n","mime":"application/vnd.sas.ods.html5"},{"data":"[\n\t{\n\t\t\"line\": \"16 ods html5;\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"NOTE: Writing HTML5 Body file: sashtml1.htm\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"17 data work.prdsale;\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"18 \\tset sashelp.PRDSALE;\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"19 run;\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"NOTE: There were 1440 observations read from the data set SASHELP.PRDSALE.\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"NOTE: The data set WORK.PRDSALE has 1440 observations and 10 variables.\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"NOTE: DATA statement used (Total process time):\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \" real time 0.00 seconds\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \" cpu time 0.01 seconds\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \" \",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"20 \",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"21 proc means data=work.prdsale;\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"22 run;\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"NOTE: There were 1440 observations read from the data set WORK.PRDSALE.\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"NOTE: The PROCEDURE MEANS printed page 1.\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"NOTE: PROCEDURE MEANS used (Total process time):\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \" real time 0.04 seconds\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \" cpu time 0.05 seconds\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \" \",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"23 ;run;quit;ods html5 close;\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t}\n]","mime":"application/vnd.sas.compute.log.lines"}]}]},{"kind":1,"language":"markdown","value":"## SQL Code","outputs":[]},{"kind":2,"language":"sql","value":"CREATE TABLE WORK.QUERY_PRDSALE AS\n SELECT\n (t1.COUNTRY) LABEL='Country' FORMAT=$CHAR10.,\n (SUM(t1.ACTUAL)) FORMAT=DOLLAR12.2 LENGTH=8 AS SUM_ACTUAL\n FROM\n WORK.PRDSALE t1\n GROUP BY\n t1.COUNTRY","outputs":[{"items":[{"data":"[\n\t{\n\t\t\"line\": \"24 ods html5;\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"NOTE: Writing HTML5 Body file: sashtml2.htm\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"25 proc sql;\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"26 CREATE TABLE WORK.QUERY_PRDSALE AS\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"27 SELECT\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"28 (t1.COUNTRY) LABEL='Country' FORMAT=$CHAR10.,\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"29 (SUM(t1.ACTUAL)) FORMAT=DOLLAR12.2 LENGTH=8 AS SUM_ACTUAL\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"30 FROM\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"31 WORK.PRDSALE t1\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"32 GROUP BY\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"33 t1.COUNTRY\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"34 ;\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"NOTE: Table WORK.QUERY_PRDSALE created, with 3 rows and 2 columns.\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"\",\n\t\t\"type\": \"normal\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"34 ! quit;\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"NOTE: PROCEDURE SQL used (Total process time):\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \" real time 0.00 seconds\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \" cpu time 0.00 seconds\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \" \",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"6 The SAS System Monday, August 21, 2023 02:56:00 PM\",\n\t\t\"type\": \"title\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"\",\n\t\t\"type\": \"title\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"35 ;run;quit;ods html5 close;\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t}\n]","mime":"application/vnd.sas.compute.log.lines"}]}]},{"kind":1,"language":"markdown","value":"A last comment in Markdown at the end of the document","outputs":[]},{"kind":1,"language":"markdown","value":"","outputs":[]}] \ No newline at end of file +[{"kind":1,"language":"markdown","value":"# Notebook to SAS Test","outputs":[]},{"kind":1,"language":"markdown","value":"## Python Code\n\nThis is some Python code","outputs":[]},{"kind":1,"language":"markdown","value":"This is a separate note in **Markdown** format.","outputs":[]},{"kind":2,"language":"python","value":"a, b = 4, 2\nprint(\"Result: \", a*10 + b)","outputs":[{"items":[{"data":"[\n\t{\n\t\t\"line\": \"14 /** LOG_START_INDICATOR **/\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"15 title;footnote;ods _all_ close;\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"16 ods graphics on;\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"17 ods html5(id=vscode) style=Ignite options(bitmap_mode='inline' svg_mode='inline');\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"NOTE: Writing HTML5(VSCODE) Body file: sashtml1.htm\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"18 %let _SASPROGRAMFILE = %nrquote(%nrstr(/Users/elreid/personalGit/vscode-sas-extension/client/testFixture/sasnb_export.sasnb));\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"19 proc python;\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"20 submit\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"NOTE: Python initialized.\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"Python 3.11.10 (main, Nov 30 2025, 14:30:37) [GCC 11.5.0 20240719 (Red Hat 11.5.0-11)] on linux\",\n\t\t\"type\": \"normal\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"Type \\\"help\\\", \\\"copyright\\\", \\\"credits\\\" or \\\"license\\\" for more information.\",\n\t\t\"type\": \"normal\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \">>>\",\n\t\t\"type\": \"normal\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \">>> \",\n\t\t\"type\": \"normal\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"20 ! ;\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"21 a, b = 4, 2\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"22 print(\\\"Result: \\\", a*10 + b)\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"23 endsubmit;\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"24 run;\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \">>>\",\n\t\t\"type\": \"normal\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"Result: 42\",\n\t\t\"type\": \"normal\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \">>> \",\n\t\t\"type\": \"normal\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"NOTE: PROCEDURE PYTHON used (Total process time):\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \" real time 1.99 seconds\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \" cpu time 0.08 seconds\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \" \",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"25 ;*';*\\\";*/;run;quit;ods html5(id=vscode) close;\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"26 \",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t}\n]","mime":"application/vnd.sas.compute.log.lines"}]}]},{"kind":1,"language":"markdown","value":"## R Code\n\nThis is some R code","outputs":[]},{"kind":1,"language":"markdown","value":"This is a separate note in **Markdown** format.","outputs":[]},{"kind":2,"language":"r","value":"die <- 1:6\npaste(\"Die Maths: \", die[3]*4 + die[6])","outputs":[{"items":[{"data":"[\n\t{\n\t\t\"line\": \"27 /** LOG_START_INDICATOR **/\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"28 title;footnote;ods _all_ close;\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"29 ods graphics on;\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"30 ods html5(id=vscode) style=Ignite options(bitmap_mode='inline' svg_mode='inline');\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"NOTE: Writing HTML5(VSCODE) Body file: sashtml2.htm\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"31 %let _SASPROGRAMFILE = %nrquote(%nrstr(/Users/elreid/personalGit/vscode-sas-extension/client/testFixture/sasnb_export.sasnb));\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"32 proc rlang;\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"33 submit\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"NOTE: Resuming Python state from previous PROC PYTHON invocation.\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"33 ! ;\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"34 die <- 1:6\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"35 paste(\\\"Die Maths: \\\", die[3]*4 + die[6])\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"36 endsubmit;\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"37 run;\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"[1] \\\"Die Maths: 18\\\"\",\n\t\t\"type\": \"normal\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"NOTE: PROCEDURE RLANG used (Total process time):\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \" real time 0.00 seconds\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \" cpu time 0.00 seconds\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \" \",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"38 ;*';*\\\";*/;run;quit;ods html5(id=vscode) close;\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"39 \",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t}\n]","mime":"application/vnd.sas.compute.log.lines"}]}]},{"kind":1,"language":"markdown","value":"## SAS Code","outputs":[]},{"kind":2,"language":"sas","value":"data work.prdsale;\n\tset sashelp.PRDSALE;\nrun;\n\nproc means data=work.prdsale;\nrun;","outputs":[{"items":[{"data":"\n\n\n\n\nSAS Output\n\n\n\n
\n
\n

The MEANS Procedure

\n
\n
\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
VariableLabelNMeanStd DevMinimumMaximum
\n
\n
ACTUAL
\n
PREDICT
\n
QUARTER
\n
YEAR
\n
MONTH
\n
\n
\n
\n
Actual Sales
\n
Predicted Sales
\n
Quarter
\n
Year
\n
Month
\n
\n
\n
\n
1440
\n
1440
\n
1440
\n
1440
\n
1440
\n
\n
\n
\n
507.1784722
\n
490.4826389
\n
2.5000000
\n
1993.50
\n
12403.00
\n
\n
\n
\n
287.0313065
\n
285.7667904
\n
1.1184224
\n
0.5001737
\n
210.6291578
\n
\n
\n
\n
3.0000000
\n
0
\n
1.0000000
\n
1993.00
\n
12054.00
\n
\n
\n
\n
1000.00
\n
1000.00
\n
4.0000000
\n
1994.00
\n
12753.00
\n
\n
\n
\n
\n\n\n","mime":"application/vnd.sas.ods.html5"},{"data":"[\n\t{\n\t\t\"line\": \"40 /** LOG_START_INDICATOR **/\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"3 The SAS System Friday, 5 December 2025 14:39:00\",\n\t\t\"type\": \"title\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"\",\n\t\t\"type\": \"title\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"41 title;footnote;ods _all_ close;\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"42 ods graphics on;\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"43 ods html5(id=vscode) style=Ignite options(bitmap_mode='inline' svg_mode='inline');\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"NOTE: Writing HTML5(VSCODE) Body file: sashtml3.htm\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"44 %let _SASPROGRAMFILE = %nrquote(%nrstr(/Users/elreid/personalGit/vscode-sas-extension/client/testFixture/sasnb_export.sasnb));\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"45 data work.prdsale;\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"46 \\tset sashelp.PRDSALE;\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"47 run;\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"NOTE: There were 1440 observations read from the data set SASHELP.PRDSALE.\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"NOTE: The data set WORK.PRDSALE has 1440 observations and 10 variables.\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"NOTE: DATA statement used (Total process time):\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \" real time 0.00 seconds\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \" cpu time 0.02 seconds\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \" \",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"48 \",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"49 proc means data=work.prdsale;\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"50 run;\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"NOTE: There were 1440 observations read from the data set WORK.PRDSALE.\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"NOTE: PROCEDURE MEANS used (Total process time):\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \" real time 0.05 seconds\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \" cpu time 0.05 seconds\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \" \",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"51 ;*';*\\\";*/;run;quit;ods html5(id=vscode) close;\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"52 \",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t}\n]","mime":"application/vnd.sas.compute.log.lines"}]}]},{"kind":1,"language":"markdown","value":"## SQL Code","outputs":[]},{"kind":2,"language":"sql","value":"CREATE TABLE WORK.QUERY_PRDSALE AS\n SELECT\n (t1.COUNTRY) LABEL='Country' FORMAT=$CHAR10.,\n (SUM(t1.ACTUAL)) FORMAT=DOLLAR12.2 LENGTH=8 AS SUM_ACTUAL\n FROM\n WORK.PRDSALE t1\n GROUP BY\n t1.COUNTRY","outputs":[{"items":[{"data":"[\n\t{\n\t\t\"line\": \"53 /** LOG_START_INDICATOR **/\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"54 title;footnote;ods _all_ close;\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"55 ods graphics on;\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"56 ods html5(id=vscode) style=Ignite options(bitmap_mode='inline' svg_mode='inline');\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"NOTE: Writing HTML5(VSCODE) Body file: sashtml4.htm\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"57 %let _SASPROGRAMFILE = %nrquote(%nrstr(/Users/elreid/personalGit/vscode-sas-extension/client/testFixture/sasnb_export.sasnb));\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"58 proc sql;\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"59 CREATE TABLE WORK.QUERY_PRDSALE AS\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"60 SELECT\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"61 (t1.COUNTRY) LABEL='Country' FORMAT=$CHAR10.,\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"62 (SUM(t1.ACTUAL)) FORMAT=DOLLAR12.2 LENGTH=8 AS SUM_ACTUAL\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"63 FROM\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"64 WORK.PRDSALE t1\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"65 GROUP BY\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"66 t1.COUNTRY\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"67 ;\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"NOTE: Table WORK.QUERY_PRDSALE created, with 3 rows and 2 columns.\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"\",\n\t\t\"type\": \"normal\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"67 ! quit;\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"NOTE: PROCEDURE SQL used (Total process time):\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \" real time 0.01 seconds\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \" cpu time 0.01 seconds\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \" \",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"68 ;*';*\\\";*/;run;quit;ods html5(id=vscode) close;\",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"69 \",\n\t\t\"type\": \"source\",\n\t\t\"version\": 1\n\t},\n\t{\n\t\t\"line\": \"\",\n\t\t\"type\": \"note\",\n\t\t\"version\": 1\n\t}\n]","mime":"application/vnd.sas.compute.log.lines"}]}]},{"kind":1,"language":"markdown","value":"A last comment in Markdown at the end of the document","outputs":[]},{"kind":1,"language":"markdown","value":"","outputs":[]}] \ No newline at end of file From 3b4180210e2bc8532dfb870d8710ccebfa7c3b9d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elijah=20C=C3=BAchulainn=20Reid?= <56865341+Wizzzzzzard@users.noreply.github.com> Date: Fri, 5 Dec 2025 14:41:43 +0000 Subject: [PATCH 22/34] making the rlang id match python MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Elijah Cúchulainn Reid <56865341+Wizzzzzzard@users.noreply.github.com> --- client/src/components/ContentNavigator/convert.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/src/components/ContentNavigator/convert.ts b/client/src/components/ContentNavigator/convert.ts index c64619fc6..80a03568e 100644 --- a/client/src/components/ContentNavigator/convert.ts +++ b/client/src/components/ContentNavigator/convert.ts @@ -20,7 +20,7 @@ const stepRef: Record = { sas: "a7190700-f59c-4a94-afe2-214ce639fcde", sql: "a7190700-f59c-4a94-afe2-214ce639fcde", python: "ab59f8c4-af9a-4608-a5d5-a8365357bb99", - rlang: "ab59f8c4-af9a-4608-a5d6-a8365357bb98", + rlang: "ab59f8c4-af9a-4608-a5d5-a8365357bb99", }; const stepTitle: Record = { From 54f72e365e05e35e3dd102820ef2be616e5900e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elijah=20C=C3=BAchulainn=20Reid?= <56865341+Wizzzzzzard@users.noreply.github.com> Date: Fri, 5 Dec 2025 14:57:50 +0000 Subject: [PATCH 23/34] Updating references to languages in docs to include R MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Elijah Cúchulainn Reid <56865341+Wizzzzzzard@users.noreply.github.com> --- README.md | 2 +- website/docs/Features/sasNotebook.md | 2 +- website/docs/README.md | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 122544562..ba6de89fd 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ The SAS extension includes many [features](https://sassoftware.github.io/vscode- - SAS syntax highlighting and help, code completion, and code snippets - Navigate SAS Content and libraries, including table viewer -- Create notebooks for SAS, SQL, Python and other languages +- Create notebooks for SAS, SQL, Python, R, and other languages diff --git a/website/docs/Features/sasNotebook.md b/website/docs/Features/sasNotebook.md index c2e7afb8d..01066361d 100644 --- a/website/docs/Features/sasNotebook.md +++ b/website/docs/Features/sasNotebook.md @@ -25,7 +25,7 @@ To export your SAS Notebook to other formats, click the **More Actions** (`...`) ### SAS -PYTHON and SQL code cells will be wrapped with PROC PYTHON/SQL respectively to be executed on SAS. Markdown cells will be converted to block comments. +PYTHON, R, and SQL code cells will be wrapped with PROC PYTHON/RLANG/SQL respectively to be executed on SAS. Markdown cells will be converted to block comments. ### HTML diff --git a/website/docs/README.md b/website/docs/README.md index e54c39d6e..8955ff2ef 100644 --- a/website/docs/README.md +++ b/website/docs/README.md @@ -16,4 +16,4 @@ The SAS extension includes the following features: - Access to SAS Content and libraries -- Ability to create notebooks for SAS, SQL, Python, and other languages +- Ability to create notebooks for SAS, SQL, Python, R, and other languages From 82d5f267641ed576d6c3f3538411cbb4cb6332e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elijah=20C=C3=BAchulainn=20Reid?= <56865341+Wizzzzzzard@users.noreply.github.com> Date: Fri, 5 Dec 2025 14:59:52 +0000 Subject: [PATCH 24/34] Including R changes in the Changelog MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Elijah Cúchulainn Reid <56865341+Wizzzzzzard@users.noreply.github.com> --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 96aaddbc9..f55ed822b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), - Add the ability to sort columns in data viewer and view column properties ([#1622](https://github.com/sassoftware/vscode-sas-extension/pull/1622)) - Add code comment collapsing ([#1638](https://github.com/sassoftware/vscode-sas-extension/pull/1638)) - Add ability to view dataset properties ([#1631](https://github.com/sassoftware/vscode-sas-extension/pull/1631)) +- Add R language support for PROC RLANG (syntax highlighting, notebook cells, code formatting preservation) ### Fixed From ca092d23ecc44316221bb5a4cba4d1d9b3876ba6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elijah=20C=C3=BAchulainn=20Reid?= <56865341+Wizzzzzzard@users.noreply.github.com> Date: Fri, 5 Dec 2025 15:20:04 +0000 Subject: [PATCH 25/34] removing rlang case now that references have been updated to r MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Elijah Cúchulainn Reid <56865341+Wizzzzzzard@users.noreply.github.com> --- client/src/components/notebook/exporters/toSAS.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/client/src/components/notebook/exporters/toSAS.ts b/client/src/components/notebook/exporters/toSAS.ts index 613050afc..d90674bf1 100644 --- a/client/src/components/notebook/exporters/toSAS.ts +++ b/client/src/components/notebook/exporters/toSAS.ts @@ -17,8 +17,6 @@ const exportCell = (cell: NotebookCell) => { return wrapPython(text); case "r": return wrapRlang(text); - case "rlang": - return wrapRlang(text); case "sql": return wrapSQL(text); case "markdown": From adc239c32fdf73ce8fbe71023d42c679a7f1990b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elijah=20C=C3=BAchulainn=20Reid?= <56865341+Wizzzzzzard@users.noreply.github.com> Date: Fri, 5 Dec 2025 15:21:55 +0000 Subject: [PATCH 26/34] removing debug function from CodeZoneManager MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Elijah Cúchulainn Reid <56865341+Wizzzzzzard@users.noreply.github.com> --- server/src/sas/CodeZoneManager.ts | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/server/src/sas/CodeZoneManager.ts b/server/src/sas/CodeZoneManager.ts index 6ba023c89..a55717882 100644 --- a/server/src/sas/CodeZoneManager.ts +++ b/server/src/sas/CodeZoneManager.ts @@ -2027,17 +2027,16 @@ export class CodeZoneManager { const zone = this._zone(stack, context); return zone.type; } - - // Helper function for debugging zone types - static getZoneTypeName(zone: number): string { - for (const [attr, value] of Object.entries(CodeZoneManager.ZONE_TYPE)) { - if (value === zone) { - return attr; - } - } - return `UNKNOWN(${zone})`; - } - + //only for debug + //function _getZoneName(zone) { + // for(var attr in ZONE_TYPE) { + // if (ZONE_TYPE.hasOwnProperty(attr)) { + // if (ZONE_TYPE[attr] === zone) { + // return attr; + // } + // } + // } + //} private _datasetOptions(context: Context) { let token1 = this._getNextEx(context), equal, From 18cba798412d160f6030cfa6b60358e578531b07 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elijah=20C=C3=BAchulainn=20Reid?= <56865341+Wizzzzzzard@users.noreply.github.com> Date: Fri, 5 Dec 2025 15:23:07 +0000 Subject: [PATCH 27/34] Simplifying R comment/string regex in Lexer MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Elijah Cúchulainn Reid <56865341+Wizzzzzzard@users.noreply.github.com> --- server/src/sas/Lexer.ts | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/server/src/sas/Lexer.ts b/server/src/sas/Lexer.ts index c0797c991..f8cfd09ec 100644 --- a/server/src/sas/Lexer.ts +++ b/server/src/sas/Lexer.ts @@ -439,11 +439,7 @@ export class Lexer { ).exec(lineContent.substring(pos)); if (match) { const matchedText = match[0]; - if ( - matchedText.startsWith("'") || - matchedText.startsWith('"') || - matchedText.startsWith("#") - ) { + if (/^('|"|#)/.test(matchedText)) { // do nothing to skip string and single line comment } else { token = this._foundEmbeddedCodeToken(this.curr, { From 7901b2b1c84443ff88b0e231667383513dc7120a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elijah=20C=C3=BAchulainn=20Reid?= <56865341+Wizzzzzzard@users.noreply.github.com> Date: Fri, 5 Dec 2025 16:25:42 +0000 Subject: [PATCH 28/34] turning on git always sign off in vscode settings to help with DCO Issues MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Elijah Cúchulainn Reid <56865341+Wizzzzzzard@users.noreply.github.com> --- .vscode/settings.json | 1 + 1 file changed, 1 insertion(+) diff --git a/.vscode/settings.json b/.vscode/settings.json index e1c7ca9c0..be54e51e9 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -3,6 +3,7 @@ "editor.tabSize": 2, "editor.insertSpaces": true, "editor.formatOnSave": true, + "git.alwaysSignOff": true, "typescript.tsc.autoDetect": "off", "typescript.preferences.quoteStyle": "single" } From 3eab50d07d27462fa847c821e1f20e4c6c1eee1e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elijah=20C=C3=BAchulainn=20Reid?= <56865341+Wizzzzzzard@users.noreply.github.com> Date: Mon, 8 Dec 2025 14:50:58 +0000 Subject: [PATCH 29/34] Add R code extraction utilities and type definitions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Elijah Cúchulainn Reid <56865341+Wizzzzzzard@users.noreply.github.com> --- server/src/r/RLanguageProvider.ts | 14 ++ server/src/r/utils.ts | 80 +++++++ .../test/python/extract_python_codes.test.ts | 158 ++++++++++++++ server/test/r/extract_r_codes.test.ts | 206 ++++++++++++++++++ 4 files changed, 458 insertions(+) create mode 100644 server/src/r/RLanguageProvider.ts create mode 100644 server/src/r/utils.ts create mode 100644 server/test/python/extract_python_codes.test.ts create mode 100644 server/test/r/extract_r_codes.test.ts diff --git a/server/src/r/RLanguageProvider.ts b/server/src/r/RLanguageProvider.ts new file mode 100644 index 000000000..4e85418c8 --- /dev/null +++ b/server/src/r/RLanguageProvider.ts @@ -0,0 +1,14 @@ +// Copyright © 2024, SAS Institute Inc., Cary, NC, USA. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +import type { RLanguageProviderBrowser } from "./browser/RLanguageProviderBrowser"; +import type { RLanguageProviderNode } from "./node/RLanguageProviderNode"; + +/** + * Union type for R Language Provider implementations. + * + * - Node: Full R language server support (spawns external R process) + * - Browser: Stub implementation (R not available in WASM yet) + */ +export type RLanguageProvider = + | RLanguageProviderNode + | RLanguageProviderBrowser; diff --git a/server/src/r/utils.ts b/server/src/r/utils.ts new file mode 100644 index 000000000..a65b1c1e2 --- /dev/null +++ b/server/src/r/utils.ts @@ -0,0 +1,80 @@ +// Copyright © 2024, SAS Institute Inc., Cary, NC, USA. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +import { DocumentSymbol } from "vscode-languageserver"; +import { TextDocument } from "vscode-languageserver-textdocument"; + +import { CodeZoneManager } from "../sas/CodeZoneManager"; +import { LanguageServiceProvider } from "../sas/LanguageServiceProvider"; +import { isCustomRegionStartComment } from "../sas/utils"; + +/** + * Extracts R code from PROC RLANG blocks in a SAS document. + * This converts the SAS document with embedded R into a pure R document + * that the R language server can analyze. + */ +export const extractRCodes = ( + doc: TextDocument, + languageService: LanguageServiceProvider, +): string => { + const codeZoneManager = languageService.getCodeZoneManager(); + const rDocLines: string[] = []; + const symbols: DocumentSymbol[] = languageService.getDocumentSymbols(); + + for (let i = 0; i < symbols.length; i++) { + const symbol = symbols[i]; + if (isCustomRegionStartComment(symbol.name)) { + symbols.splice(i + 1, 0, ...(symbol.children ?? [])); + } + if (symbol.name?.toUpperCase() !== "PROC RLANG") { + continue; + } + + let rCodeStart = undefined; + let rCodeEnd = undefined; + const pos = { ...symbol.range.start }; + + while (pos.line <= symbol.range.end.line) { + if ( + !rCodeStart && + codeZoneManager.getCurrentZone(pos.line, pos.character) === + CodeZoneManager.ZONE_TYPE.EMBEDDED_LANG + ) { + rCodeStart = { ...pos }; + } + if ( + rCodeStart && + codeZoneManager.getCurrentZone(pos.line, pos.character) !== + CodeZoneManager.ZONE_TYPE.EMBEDDED_LANG + ) { + rCodeEnd = { ...pos }; + if (rCodeEnd.character === 0) { + rCodeEnd.line--; + rCodeEnd.character = + languageService.model.getColumnCount(rCodeEnd.line) - 1; + if (rCodeEnd.character < 0) { + rCodeEnd.character = 0; + } + } + break; + } + pos.line++; + pos.character = 0; + } + + if (!rCodeStart) { + continue; + } + if (!rCodeEnd) { + rCodeEnd = { ...symbol.range.end }; + } + + const rCode = doc.getText({ + start: rCodeStart, + end: rCodeEnd, + }); + + rDocLines.push(rCode); + } + + return rDocLines.join("\n"); +}; diff --git a/server/test/python/extract_python_codes.test.ts b/server/test/python/extract_python_codes.test.ts new file mode 100644 index 000000000..f6f036d71 --- /dev/null +++ b/server/test/python/extract_python_codes.test.ts @@ -0,0 +1,158 @@ +// Copyright © 2024, SAS Institute Inc., Cary, NC, USA. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +import { TextDocument } from "vscode-languageserver-textdocument"; + +import { assert } from "chai"; + +import { LanguageServiceProvider } from "../../src/sas/LanguageServiceProvider"; +import { extractPythonCodes } from "../../src/python/utils"; + +describe("Python Code Extraction", () => { + it("extracts simple Python code from PROC PYTHON", () => { + const sasCode = `proc python; +submit; +x = [1, 2, 3] +print(sum(x)) +endsubmit; +run;`; + + const doc = TextDocument.create("test.sas", "sas", 1, sasCode); + const languageService = new LanguageServiceProvider(doc); + const pythonCode = extractPythonCodes(doc, languageService); + + assert.include(pythonCode, "x = [1, 2, 3]"); + assert.include(pythonCode, "print(sum(x))"); + assert.include(pythonCode, "import sas2py"); // Python includes helper import + }); + + it("extracts Python code from multiple PROC PYTHON blocks", () => { + const sasCode = `proc python; +submit; +x = list(range(10)) +endsubmit; +run; + +data _null_; + put "SAS code"; +run; + +proc python; +submit; +y = sum(x) +print(y) +endsubmit; +run;`; + + const doc = TextDocument.create("test.sas", "sas", 1, sasCode); + const languageService = new LanguageServiceProvider(doc); + const pythonCode = extractPythonCodes(doc, languageService); + + assert.include(pythonCode, "x = list(range(10))"); + assert.include(pythonCode, "y = sum(x)"); + assert.include(pythonCode, "print(y)"); + }); + + it("handles Python code with comments", () => { + const sasCode = `proc python; +submit; +# This is a Python comment +x = [1, 2, 3] +# Calculate sum +print(sum(x)) +endsubmit; +run;`; + + const doc = TextDocument.create("test.sas", "sas", 1, sasCode); + const languageService = new LanguageServiceProvider(doc); + const pythonCode = extractPythonCodes(doc, languageService); + + assert.include(pythonCode, "# This is a Python comment"); + assert.include(pythonCode, "# Calculate sum"); + }); + + it("handles Python code with triple-quoted strings", () => { + const sasCode = `proc python; +submit; +text = """Hello +World""" +print(text) +endsubmit; +run;`; + + const doc = TextDocument.create("test.sas", "sas", 1, sasCode); + const languageService = new LanguageServiceProvider(doc); + const pythonCode = extractPythonCodes(doc, languageService); + + assert.include(pythonCode, "text ="); + assert.include(pythonCode, "print(text)"); + }); + + it("handles Python code with loops and control structures", () => { + const sasCode = `proc python; +submit; +for i in range(10): + print(i) + if i == 5: + break +endsubmit; +run;`; + + const doc = TextDocument.create("test.sas", "sas", 1, sasCode); + const languageService = new LanguageServiceProvider(doc); + const pythonCode = extractPythonCodes(doc, languageService); + + assert.include(pythonCode, "for i in range(10):"); + assert.include(pythonCode, "print(i)"); + assert.include(pythonCode, "if i == 5:"); + assert.include(pythonCode, "break"); + }); + + it("handles Python code with function definitions", () => { + const sasCode = `proc python; +submit; +def my_func(x, y): + result = x + y + return result +z = my_func(5, 3) +endsubmit; +run;`; + + const doc = TextDocument.create("test.sas", "sas", 1, sasCode); + const languageService = new LanguageServiceProvider(doc); + const pythonCode = extractPythonCodes(doc, languageService); + + assert.include(pythonCode, "def my_func(x, y):"); + assert.include(pythonCode, "return result"); + assert.include(pythonCode, "z = my_func(5, 3)"); + }); + + it("returns import statement when no PROC PYTHON blocks exist", () => { + const sasCode = `data _null_; + x = 5; + put x=; +run;`; + + const doc = TextDocument.create("test.sas", "sas", 1, sasCode); + const languageService = new LanguageServiceProvider(doc); + const pythonCode = extractPythonCodes(doc, languageService); + + assert.include(pythonCode, "import sas2py"); + assert.notInclude(pythonCode, "data _null_"); + }); + + it("handles interactive mode", () => { + const sasCode = `proc python; +interactive; +import pandas as pd +df = pd.DataFrame({'x': [1, 2, 3]}) +endinteractive; +run;`; + + const doc = TextDocument.create("test.sas", "sas", 1, sasCode); + const languageService = new LanguageServiceProvider(doc); + const pythonCode = extractPythonCodes(doc, languageService); + + assert.include(pythonCode, "import pandas as pd"); + assert.include(pythonCode, "df = pd.DataFrame"); + }); +}); diff --git a/server/test/r/extract_r_codes.test.ts b/server/test/r/extract_r_codes.test.ts new file mode 100644 index 000000000..1d3b3ea46 --- /dev/null +++ b/server/test/r/extract_r_codes.test.ts @@ -0,0 +1,206 @@ +// Copyright © 2024, SAS Institute Inc., Cary, NC, USA. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +import { TextDocument } from "vscode-languageserver-textdocument"; + +import { assert } from "chai"; + +import { extractRCodes } from "../../src/r/utils"; +import { LanguageServiceProvider } from "../../src/sas/LanguageServiceProvider"; + +describe("R Code Extraction", () => { + it("extracts simple R code from PROC RLANG", () => { + const sasCode = `proc rlang; +submit; +x <- c(1, 2, 3) +mean(x) +endsubmit; +run;`; + + const doc = TextDocument.create("test.sas", "sas", 1, sasCode); + const languageService = new LanguageServiceProvider(doc); + const rCode = extractRCodes(doc, languageService); + + assert.include(rCode, "x <- c(1, 2, 3)"); + assert.include(rCode, "mean(x)"); + assert.notInclude(rCode, "proc rlang"); + assert.notInclude(rCode, "endsubmit"); + }); + + it("extracts R code from multiple PROC RLANG blocks", () => { + const sasCode = `proc rlang; +submit; +x <- 1:10 +endsubmit; +run; + +data _null_; + put "SAS code"; +run; + +proc rlang; +submit; +y <- mean(x) +print(y) +endsubmit; +run;`; + + const doc = TextDocument.create("test.sas", "sas", 1, sasCode); + const languageService = new LanguageServiceProvider(doc); + const rCode = extractRCodes(doc, languageService); + + assert.include(rCode, "x <- 1:10"); + assert.include(rCode, "y <- mean(x)"); + assert.include(rCode, "print(y)"); + assert.notInclude(rCode, 'put "SAS code"'); + }); + + it("handles R code with comments", () => { + const sasCode = `proc rlang; +submit; +# This is an R comment +x <- c(1, 2, 3) +# Calculate mean +mean(x) +endsubmit; +run;`; + + const doc = TextDocument.create("test.sas", "sas", 1, sasCode); + const languageService = new LanguageServiceProvider(doc); + const rCode = extractRCodes(doc, languageService); + + assert.include(rCode, "# This is an R comment"); + assert.include(rCode, "# Calculate mean"); + }); + + it("handles R code with multiline strings", () => { + const sasCode = `proc rlang; +submit; +text <- "Hello +World" +cat(text) +endsubmit; +run;`; + + const doc = TextDocument.create("test.sas", "sas", 1, sasCode); + const languageService = new LanguageServiceProvider(doc); + const rCode = extractRCodes(doc, languageService); + + assert.include(rCode, "text <-"); + assert.include(rCode, "cat(text)"); + }); + + it("handles R code with loops and control structures", () => { + const sasCode = `proc rlang; +submit; +for (i in 1:10) { + print(i) + if (i == 5) { + break + } +} +endsubmit; +run;`; + + const doc = TextDocument.create("test.sas", "sas", 1, sasCode); + const languageService = new LanguageServiceProvider(doc); + const rCode = extractRCodes(doc, languageService); + + assert.include(rCode, "for (i in 1:10)"); + assert.include(rCode, "print(i)"); + assert.include(rCode, "if (i == 5)"); + assert.include(rCode, "break"); + }); + + it("handles R code with function definitions", () => { + const sasCode = `proc rlang; +submit; +my_func <- function(x, y) { + result <- x + y + return(result) +} +z <- my_func(5, 3) +endsubmit; +run;`; + + const doc = TextDocument.create("test.sas", "sas", 1, sasCode); + const languageService = new LanguageServiceProvider(doc); + const rCode = extractRCodes(doc, languageService); + + assert.include(rCode, "my_func <- function(x, y)"); + assert.include(rCode, "return(result)"); + assert.include(rCode, "z <- my_func(5, 3)"); + }); + + it("returns empty string when no PROC RLANG blocks exist", () => { + const sasCode = `data _null_; + x = 5; + put x=; +run;`; + + const doc = TextDocument.create("test.sas", "sas", 1, sasCode); + const languageService = new LanguageServiceProvider(doc); + const rCode = extractRCodes(doc, languageService); + + assert.equal(rCode, ""); + }); + + it("handles empty PROC RLANG blocks", () => { + const sasCode = `proc rlang; +submit; +endsubmit; +run;`; + + const doc = TextDocument.create("test.sas", "sas", 1, sasCode); + const languageService = new LanguageServiceProvider(doc); + const rCode = extractRCodes(doc, languageService); + + assert.equal(rCode, ""); + }); + + it("handles mixed SAS and R code", () => { + const sasCode = `/* SAS comment */ +data test; + x = 1; +run; + +proc rlang; +submit; +r_data <- data.frame(x = 1:5, y = 6:10) +summary(r_data) +endsubmit; +run; + +proc print data=test; +run;`; + + const doc = TextDocument.create("test.sas", "sas", 1, sasCode); + const languageService = new LanguageServiceProvider(doc); + const rCode = extractRCodes(doc, languageService); + + assert.include(rCode, "r_data <- data.frame"); + assert.include(rCode, "summary(r_data)"); + assert.notInclude(rCode, "data test"); + assert.notInclude(rCode, "proc print"); + }); + + it("preserves R code whitespace and indentation", () => { + const sasCode = `proc rlang; +submit; +if (TRUE) { + x <- 1 + if (x > 0) { + print("positive") + } +} +endsubmit; +run;`; + + const doc = TextDocument.create("test.sas", "sas", 1, sasCode); + const languageService = new LanguageServiceProvider(doc); + const rCode = extractRCodes(doc, languageService); + + // Check that indentation is preserved + assert.include(rCode, " x <- 1"); + assert.include(rCode, ' print("positive")'); + }); +}); From 62e89e836dfcfa10eac02ca19235ab74eeffcb8a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elijah=20C=C3=BAchulainn=20Reid?= <56865341+Wizzzzzzard@users.noreply.github.com> Date: Mon, 8 Dec 2025 14:52:00 +0000 Subject: [PATCH 30/34] Add R IntelliSense for PROC RLANG blocks MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Elijah Cúchulainn Reid <56865341+Wizzzzzzard@users.noreply.github.com> --- server/package-lock.json | 1449 ++++++++++++++++- server/package.json | 3 +- server/src/r/RLanguageProvider.ts | 2 +- .../src/r/browser/RLanguageProviderBrowser.ts | 385 +++++ server/src/r/node/RLanguageProviderNode.ts | 531 ++++++ .../test/python/extract_python_codes.test.ts | 2 +- syntaxes/sas.tmLanguage.json | 25 + 7 files changed, 2330 insertions(+), 67 deletions(-) create mode 100644 server/src/r/browser/RLanguageProviderBrowser.ts create mode 100644 server/src/r/node/RLanguageProviderNode.ts diff --git a/server/package-lock.json b/server/package-lock.json index 6dfc4ed8c..71750061a 100644 --- a/server/package-lock.json +++ b/server/package-lock.json @@ -12,22 +12,566 @@ "pyright-internal-browser": "^1.1.367", "pyright-internal-node": "^1.1.367", "vscode-languageserver": "^10.0.0-next.2", - "vscode-languageserver-textdocument": "1.0.11" + "vscode-languageserver-textdocument": "1.0.11", + "webr": "^0.4.2" }, "engines": { "node": "*" } }, + "node_modules/@codemirror/autocomplete": { + "version": "6.20.0", + "resolved": "https://registry.npmjs.org/@codemirror/autocomplete/-/autocomplete-6.20.0.tgz", + "integrity": "sha512-bOwvTOIJcG5FVo5gUUupiwYh8MioPLQ4UcqbcRf7UQ98X90tCa9E1kZ3Z7tqwpZxYyOvh1YTYbmZE9RTfTp5hg==", + "license": "MIT", + "dependencies": { + "@codemirror/language": "^6.0.0", + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.17.0", + "@lezer/common": "^1.0.0" + } + }, + "node_modules/@codemirror/commands": { + "version": "6.10.0", + "resolved": "https://registry.npmjs.org/@codemirror/commands/-/commands-6.10.0.tgz", + "integrity": "sha512-2xUIc5mHXQzT16JnyOFkh8PvfeXuIut3pslWGfsGOhxP/lpgRm9HOl/mpzLErgt5mXDovqA0d11P21gofRLb9w==", + "license": "MIT", + "dependencies": { + "@codemirror/language": "^6.0.0", + "@codemirror/state": "^6.4.0", + "@codemirror/view": "^6.27.0", + "@lezer/common": "^1.1.0" + } + }, + "node_modules/@codemirror/language": { + "version": "6.11.3", + "resolved": "https://registry.npmjs.org/@codemirror/language/-/language-6.11.3.tgz", + "integrity": "sha512-9HBM2XnwDj7fnu0551HkGdrUrrqmYq/WC5iv6nbY2WdicXdGbhR/gfbZOH73Aqj4351alY1+aoG9rCNfiwS1RA==", + "license": "MIT", + "dependencies": { + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.23.0", + "@lezer/common": "^1.1.0", + "@lezer/highlight": "^1.0.0", + "@lezer/lr": "^1.0.0", + "style-mod": "^4.0.0" + } + }, + "node_modules/@codemirror/lint": { + "version": "6.9.2", + "resolved": "https://registry.npmjs.org/@codemirror/lint/-/lint-6.9.2.tgz", + "integrity": "sha512-sv3DylBiIyi+xKwRCJAAsBZZZWo82shJ/RTMymLabAdtbkV5cSKwWDeCgtUq3v8flTaXS2y1kKkICuRYtUswyQ==", + "license": "MIT", + "dependencies": { + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.35.0", + "crelt": "^1.0.5" + } + }, + "node_modules/@codemirror/search": { + "version": "6.5.11", + "resolved": "https://registry.npmjs.org/@codemirror/search/-/search-6.5.11.tgz", + "integrity": "sha512-KmWepDE6jUdL6n8cAAqIpRmLPBZ5ZKnicE8oGU/s3QrAVID+0VhLFrzUucVKHG5035/BSykhExDL/Xm7dHthiA==", + "license": "MIT", + "dependencies": { + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.0.0", + "crelt": "^1.0.5" + } + }, + "node_modules/@codemirror/state": { + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/@codemirror/state/-/state-6.5.2.tgz", + "integrity": "sha512-FVqsPqtPWKVVL3dPSxy8wEF/ymIEuVzF1PK3VbUgrxXpJUSHQWWZz4JMToquRxnkw+36LTamCZG2iua2Ptq0fA==", + "license": "MIT", + "dependencies": { + "@marijn/find-cluster-break": "^1.0.0" + } + }, + "node_modules/@codemirror/view": { + "version": "6.38.8", + "resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.38.8.tgz", + "integrity": "sha512-XcE9fcnkHCbWkjeKyi0lllwXmBLtyYb5dt89dJyx23I9+LSh5vZDIuk7OLG4VM1lgrXZQcY6cxyZyk5WVPRv/A==", + "license": "MIT", + "dependencies": { + "@codemirror/state": "^6.5.0", + "crelt": "^1.0.6", + "style-mod": "^4.1.0", + "w3c-keyname": "^2.2.4" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.1.tgz", + "integrity": "sha512-HHB50pdsBX6k47S4u5g/CaLjqS3qwaOVE5ILsq64jyzgMhLuCuZ8rGzM9yhsAjfjkbgUPMzZEPa7DAp7yz6vuA==", + "cpu": [ + "ppc64" + ], + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.1.tgz", + "integrity": "sha512-kFqa6/UcaTbGm/NncN9kzVOODjhZW8e+FRdSeypWe6j33gzclHtwlANs26JrupOntlcWmB0u8+8HZo8s7thHvg==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.1.tgz", + "integrity": "sha512-45fuKmAJpxnQWixOGCrS+ro4Uvb4Re9+UTieUY2f8AEc+t7d4AaZ6eUJ3Hva7dtrxAAWHtlEFsXFMAgNnGU9uQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.1.tgz", + "integrity": "sha512-LBEpOz0BsgMEeHgenf5aqmn/lLNTFXVfoWMUox8CtWWYK9X4jmQzWjoGoNb8lmAYml/tQ/Ysvm8q7szu7BoxRQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.1.tgz", + "integrity": "sha512-veg7fL8eMSCVKL7IW4pxb54QERtedFDfY/ASrumK/SbFsXnRazxY4YykN/THYqFnFwJ0aVjiUrVG2PwcdAEqQQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.1.tgz", + "integrity": "sha512-+3ELd+nTzhfWb07Vol7EZ+5PTbJ/u74nC6iv4/lwIU99Ip5uuY6QoIf0Hn4m2HoV0qcnRivN3KSqc+FyCHjoVQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.1.tgz", + "integrity": "sha512-/8Rfgns4XD9XOSXlzUDepG8PX+AVWHliYlUkFI3K3GB6tqbdjYqdhcb4BKRd7C0BhZSoaCxhv8kTcBrcZWP+xg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.1.tgz", + "integrity": "sha512-GITpD8dK9C+r+5yRT/UKVT36h/DQLOHdwGVwwoHidlnA168oD3uxA878XloXebK4Ul3gDBBIvEdL7go9gCUFzQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.1.tgz", + "integrity": "sha512-ieMID0JRZY/ZeCrsFQ3Y3NlHNCqIhTprJfDgSB3/lv5jJZ8FX3hqPyXWhe+gvS5ARMBJ242PM+VNz/ctNj//eA==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.1.tgz", + "integrity": "sha512-W9//kCrh/6in9rWIBdKaMtuTTzNj6jSeG/haWBADqLLa9P8O5YSRDzgD5y9QBok4AYlzS6ARHifAb75V6G670Q==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.1.tgz", + "integrity": "sha512-VIUV4z8GD8rtSVMfAj1aXFahsi/+tcoXXNYmXgzISL+KB381vbSTNdeZHHHIYqFyXcoEhu9n5cT+05tRv13rlw==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.1.tgz", + "integrity": "sha512-l4rfiiJRN7sTNI//ff65zJ9z8U+k6zcCg0LALU5iEWzY+a1mVZ8iWC1k5EsNKThZ7XCQ6YWtsZ8EWYm7r1UEsg==", + "cpu": [ + "loong64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.1.tgz", + "integrity": "sha512-U0bEuAOLvO/DWFdygTHWY8C067FXz+UbzKgxYhXC0fDieFa0kDIra1FAhsAARRJbvEyso8aAqvPdNxzWuStBnA==", + "cpu": [ + "mips64el" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.1.tgz", + "integrity": "sha512-NzdQ/Xwu6vPSf/GkdmRNsOfIeSGnh7muundsWItmBsVpMoNPVpM61qNzAVY3pZ1glzzAxLR40UyYM23eaDDbYQ==", + "cpu": [ + "ppc64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.1.tgz", + "integrity": "sha512-7zlw8p3IApcsN7mFw0O1Z1PyEk6PlKMu18roImfl3iQHTnr/yAfYv6s4hXPidbDoI2Q0pW+5xeoM4eTCC0UdrQ==", + "cpu": [ + "riscv64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.1.tgz", + "integrity": "sha512-cGj5wli+G+nkVQdZo3+7FDKC25Uh4ZVwOAK6A06Hsvgr8WqBBuOy/1s+PUEd/6Je+vjfm6stX0kmib5b/O2Ykw==", + "cpu": [ + "s390x" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.1.tgz", + "integrity": "sha512-z3H/HYI9MM0HTv3hQZ81f+AKb+yEoCRlUby1F80vbQ5XdzEMyY/9iNlAmhqiBKw4MJXwfgsh7ERGEOhrM1niMA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.1.tgz", + "integrity": "sha512-wzC24DxAvk8Em01YmVXyjl96Mr+ecTPyOuADAvjGg+fyBpGmxmcr2E5ttf7Im8D0sXZihpxzO1isus8MdjMCXQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.1.tgz", + "integrity": "sha512-1YQ8ybGi2yIXswu6eNzJsrYIGFpnlzEWRl6iR5gMgmsrR0FcNoV1m9k9sc3PuP5rUBLshOZylc9nqSgymI+TYg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.1.tgz", + "integrity": "sha512-5Z+DzLCrq5wmU7RDaMDe2DVXMRm2tTDvX2KU14JJVBN2CT/qov7XVix85QoJqHltpvAOZUAc3ndU56HSMWrv8g==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.1.tgz", + "integrity": "sha512-Q73ENzIdPF5jap4wqLtsfh8YbYSZ8Q0wnxplOlZUOyZy7B4ZKW8DXGWgTCZmF8VWD7Tciwv5F4NsRf6vYlZtqg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.1.tgz", + "integrity": "sha512-ajbHrGM/XiK+sXM0JzEbJAen+0E+JMQZ2l4RR4VFwvV9JEERx+oxtgkpoKv1SevhjavK2z2ReHk32pjzktWbGg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.1.tgz", + "integrity": "sha512-IPUW+y4VIjuDVn+OMzHc5FV4GubIwPnsz6ubkvN8cuhEqH81NovB53IUlrlBkPMEPxvNnf79MGBoz8rZ2iW8HA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.1.tgz", + "integrity": "sha512-RIVRWiljWA6CdVu8zkWcRmGP7iRRIIwvhDKem8UMBjPql2TXM5PkDVvvrzMtj1V+WFPB4K7zkIGM7VzRtFkjdg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.1.tgz", + "integrity": "sha512-2BR5M8CPbptC1AK5JbJT1fWrHLvejwZidKx3UMSF0ecHMa+smhi16drIrCEggkgviBwLYd5nwrFLSl5Kho96RQ==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.1.tgz", + "integrity": "sha512-d5X6RMYv6taIymSk8JBP+nxv8DQAMY6A51GPgusqLdK9wBz5wWIXy1KjTck6HnjE9hqJzJRdk+1p/t5soSbCtw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, "node_modules/@iarna/toml": { "version": "2.2.5", "resolved": "https://registry.npmjs.org/@iarna/toml/-/toml-2.2.5.tgz", "integrity": "sha512-trnsAYxU3xnS1gPHPyU961coFyLkh4gAD/0zQ5mymY4yOZ+CYvsPqUbOFSw0aDM4y0tV7tiFxL/1XfXPNC6IPg==" }, + "node_modules/@lezer/common": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@lezer/common/-/common-1.4.0.tgz", + "integrity": "sha512-DVeMRoGrgn/k45oQNu189BoW4SZwgZFzJ1+1TV5j2NJ/KFC83oa/enRqZSGshyeMk5cPWMhsKs9nx+8o0unwGg==", + "license": "MIT" + }, + "node_modules/@lezer/highlight": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@lezer/highlight/-/highlight-1.2.3.tgz", + "integrity": "sha512-qXdH7UqTvGfdVBINrgKhDsVTJTxactNNxLk7+UMwZhU13lMHaOBlJe9Vqp907ya56Y3+ed2tlqzys7jDkTmW0g==", + "license": "MIT", + "dependencies": { + "@lezer/common": "^1.3.0" + } + }, + "node_modules/@lezer/lr": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/@lezer/lr/-/lr-1.4.4.tgz", + "integrity": "sha512-LHL17Mq0OcFXm1pGQssuGTQFPPdxARjKM8f7GA5+sGtHi0K3R84YaSbmche0+RKWHnCsx9asEe5OWOI4FHfe4A==", + "license": "MIT", + "dependencies": { + "@lezer/common": "^1.0.0" + } + }, + "node_modules/@marijn/find-cluster-break": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@marijn/find-cluster-break/-/find-cluster-break-1.0.2.tgz", + "integrity": "sha512-l0h88YhZFyKdXIFNfSWpyjStDjGHwZ/U7iobcK1cQQD8sejsONdQtTVU+1wVN1PBw40PiiHB1vA5S7VTfQiP9g==", + "license": "MIT" + }, + "node_modules/@msgpack/msgpack": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/@msgpack/msgpack/-/msgpack-2.8.0.tgz", + "integrity": "sha512-h9u4u/jiIRKbq25PM+zymTyW6bhTzELvOoUd+AvYriWOAKpLGnIamaET3pnHYoI5iYphAHBI4ayx0MehR+VVPQ==", + "license": "ISC", + "engines": { + "node": ">= 10" + } + }, "node_modules/@types/emscripten": { "version": "1.39.12", "resolved": "https://registry.npmjs.org/@types/emscripten/-/emscripten-1.39.12.tgz", "integrity": "sha512-AQImDBgudQfMqUBfrjZYilRxoHDzTBp+ejh+g1fY67eSMalwIKtBXofjpyI0JBgNpHGzxeGAR2QDya0wxW9zbA==" }, + "node_modules/@xterm/xterm": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@xterm/xterm/-/xterm-5.5.0.tgz", + "integrity": "sha512-hqJHYaQb5OptNunnyAnkHyM8aCjZ1MEIDTQu1iIbbTD/xops91NB5yq1ZK/dC2JDbVWtF23zUtl9JE2NqwT87A==", + "license": "MIT", + "peer": true + }, "node_modules/@yarnpkg/fslib": { "version": "2.10.4", "resolved": "https://registry.npmjs.org/@yarnpkg/fslib/-/fslib-2.10.4.tgz", @@ -52,6 +596,15 @@ "node": ">=12 <14 || 14.2 - 14.9 || >14.10.0" } }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/ansi-styles": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", @@ -151,6 +704,38 @@ "fsevents": "~2.3.2" } }, + "node_modules/classnames": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.5.1.tgz", + "integrity": "sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow==", + "license": "MIT", + "peer": true + }, + "node_modules/codemirror": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/codemirror/-/codemirror-6.0.2.tgz", + "integrity": "sha512-VhydHotNW5w1UGK0Qj96BwSk/Zqbp9WbnyK2W/eVMv4QyF41INRGpjUhFJY7/uDNuudSc33a/PKr4iDqRduvHw==", + "license": "MIT", + "dependencies": { + "@codemirror/autocomplete": "^6.0.0", + "@codemirror/commands": "^6.0.0", + "@codemirror/language": "^6.0.0", + "@codemirror/lint": "^6.0.0", + "@codemirror/search": "^6.0.0", + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.0.0" + } + }, + "node_modules/codemirror-lang-r": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/codemirror-lang-r/-/codemirror-lang-r-0.1.1.tgz", + "integrity": "sha512-ke9Bm7IPKOoEk0p8LxZJaRlqp8CGOOZns9eKyj/WUaNV58h4uEeWbMpWeJJhVIPvfiuXYkv4FG1hD70gguWJLQ==", + "license": "MIT", + "dependencies": { + "@codemirror/language": "^6.10.3", + "lezer-r": "^0.1.3" + } + }, "node_modules/color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", @@ -181,6 +766,74 @@ "node": ">=4.0.0" } }, + "node_modules/core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", + "license": "MIT" + }, + "node_modules/crelt": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/crelt/-/crelt-1.0.6.tgz", + "integrity": "sha512-VQ2MBenTq1fWZUH9DJNGti7kKv6EeAuYr3cLwxUWhIu1baTaXh4Ib5W2CqHVqib4/MqbYGJqiL3Zb8GJZr3l4g==", + "license": "MIT" + }, + "node_modules/detect-libc": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", + "license": "Apache-2.0", + "engines": { + "node": ">=8" + } + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "license": "MIT" + }, + "node_modules/esbuild": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.1.tgz", + "integrity": "sha512-yY35KZckJJuVVPXpvjgxiCuVEJT67F6zDeVTv4rizyPrfGBUpZQsvmxnN+C371c2esD/hNMjj4tpBhuueLN7aA==", + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.27.1", + "@esbuild/android-arm": "0.27.1", + "@esbuild/android-arm64": "0.27.1", + "@esbuild/android-x64": "0.27.1", + "@esbuild/darwin-arm64": "0.27.1", + "@esbuild/darwin-x64": "0.27.1", + "@esbuild/freebsd-arm64": "0.27.1", + "@esbuild/freebsd-x64": "0.27.1", + "@esbuild/linux-arm": "0.27.1", + "@esbuild/linux-arm64": "0.27.1", + "@esbuild/linux-ia32": "0.27.1", + "@esbuild/linux-loong64": "0.27.1", + "@esbuild/linux-mips64el": "0.27.1", + "@esbuild/linux-ppc64": "0.27.1", + "@esbuild/linux-riscv64": "0.27.1", + "@esbuild/linux-s390x": "0.27.1", + "@esbuild/linux-x64": "0.27.1", + "@esbuild/netbsd-arm64": "0.27.1", + "@esbuild/netbsd-x64": "0.27.1", + "@esbuild/openbsd-arm64": "0.27.1", + "@esbuild/openbsd-x64": "0.27.1", + "@esbuild/openharmony-arm64": "0.27.1", + "@esbuild/sunos-x64": "0.27.1", + "@esbuild/win32-arm64": "0.27.1", + "@esbuild/win32-ia32": "0.27.1", + "@esbuild/win32-x64": "0.27.1" + } + }, "node_modules/fill-range": { "version": "7.1.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", @@ -189,101 +842,432 @@ "to-regex-range": "^5.0.1" }, "engines": { - "node": ">=8" + "node": ">=8" + } + }, + "node_modules/find-replace": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-replace/-/find-replace-3.0.0.tgz", + "integrity": "sha512-6Tb2myMioCAgv5kfvP5/PkZZ/ntTpVK39fHY7WkWBgvbeE+VHd/tZuZ4mrC+bxh4cfOZeYKVPaJIZtZXV7GNCQ==", + "dependencies": { + "array-back": "^3.0.1" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/get-tsconfig": { + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.13.0.tgz", + "integrity": "sha512-1VKTZJCwBrvbd+Wn3AOgQP/2Av+TfTCOlE4AcRJE72W1ksZXbAx8PPBR9RzgTeSPzlPMHrbANMH3LbltH73wxQ==", + "license": "MIT", + "dependencies": { + "resolve-pkg-maps": "^1.0.0" + }, + "funding": { + "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" + } + }, + "node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/immediate": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz", + "integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==", + "license": "MIT" + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "license": "ISC" + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "license": "MIT" + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "license": "MIT" + }, + "node_modules/jsonc-parser": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.2.1.tgz", + "integrity": "sha512-AilxAyFOAcK5wA1+LeaySVBrHsGQvUFCDWXKpZjzaL0PqW+xfBOttn8GNtWKFWqneyMZj41MWF9Kl6iPWLwgOA==" + }, + "node_modules/jszip": { + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.10.1.tgz", + "integrity": "sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==", + "license": "(MIT OR GPL-3.0-or-later)", + "dependencies": { + "lie": "~3.3.0", + "pako": "~1.0.2", + "readable-stream": "~2.3.6", + "setimmediate": "^1.0.5" + } + }, + "node_modules/jszip/node_modules/pako": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", + "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==", + "license": "(MIT AND Zlib)" + }, + "node_modules/leven": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", + "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", + "engines": { + "node": ">=6" + } + }, + "node_modules/lezer-r": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/lezer-r/-/lezer-r-0.1.3.tgz", + "integrity": "sha512-tk+7Q54+ZYHKlLZj69GuZNC8+nMYPIFhGjrSe2fTyQAk9GyUsxgRsmF8V4r7cUiB65+lRu5/SrePeTEKQx5ZAQ==", + "license": "MIT", + "dependencies": { + "@lezer/highlight": "^1.2.1", + "@lezer/lr": "^1.4.2" + } + }, + "node_modules/lie": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz", + "integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==", + "license": "MIT", + "dependencies": { + "immediate": "~3.0.5" + } + }, + "node_modules/lightningcss": { + "version": "1.30.2", + "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.30.2.tgz", + "integrity": "sha512-utfs7Pr5uJyyvDETitgsaqSyjCb2qNRAtuqUeWIAKztsOYdcACf2KtARYXg2pSvhkt+9NfoaNY7fxjl6nuMjIQ==", + "license": "MPL-2.0", + "dependencies": { + "detect-libc": "^2.0.3" + }, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "lightningcss-android-arm64": "1.30.2", + "lightningcss-darwin-arm64": "1.30.2", + "lightningcss-darwin-x64": "1.30.2", + "lightningcss-freebsd-x64": "1.30.2", + "lightningcss-linux-arm-gnueabihf": "1.30.2", + "lightningcss-linux-arm64-gnu": "1.30.2", + "lightningcss-linux-arm64-musl": "1.30.2", + "lightningcss-linux-x64-gnu": "1.30.2", + "lightningcss-linux-x64-musl": "1.30.2", + "lightningcss-win32-arm64-msvc": "1.30.2", + "lightningcss-win32-x64-msvc": "1.30.2" + } + }, + "node_modules/lightningcss-android-arm64": { + "version": "1.30.2", + "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.30.2.tgz", + "integrity": "sha512-BH9sEdOCahSgmkVhBLeU7Hc9DWeZ1Eb6wNS6Da8igvUwAe0sqROHddIlvU06q3WyXVEOYDZ6ykBZQnjTbmo4+A==", + "cpu": [ + "arm64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" } }, - "node_modules/find-replace": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-replace/-/find-replace-3.0.0.tgz", - "integrity": "sha512-6Tb2myMioCAgv5kfvP5/PkZZ/ntTpVK39fHY7WkWBgvbeE+VHd/tZuZ4mrC+bxh4cfOZeYKVPaJIZtZXV7GNCQ==", - "dependencies": { - "array-back": "^3.0.1" - }, + "node_modules/lightningcss-darwin-arm64": { + "version": "1.30.2", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.30.2.tgz", + "integrity": "sha512-ylTcDJBN3Hp21TdhRT5zBOIi73P6/W0qwvlFEk22fkdXchtNTOU4Qc37SkzV+EKYxLouZ6M4LG9NfZ1qkhhBWA==", + "cpu": [ + "arm64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], "engines": { - "node": ">=4.0.0" + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" } }, - "node_modules/fsevents": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "hasInstallScript": true, + "node_modules/lightningcss-darwin-x64": { + "version": "1.30.2", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.30.2.tgz", + "integrity": "sha512-oBZgKchomuDYxr7ilwLcyms6BCyLn0z8J0+ZZmfpjwg9fRVZIR5/GMXd7r9RH94iDhld3UmSjBM6nXWM2TfZTQ==", + "cpu": [ + "x64" + ], + "license": "MPL-2.0", "optional": true, "os": [ "darwin" ], "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" } }, - "node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dependencies": { - "is-glob": "^4.0.1" - }, + "node_modules/lightningcss-freebsd-x64": { + "version": "1.30.2", + "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.30.2.tgz", + "integrity": "sha512-c2bH6xTrf4BDpK8MoGG4Bd6zAMZDAXS569UxCAGcA7IKbHNMlhGQ89eRmvpIUGfKWNVdbhSbkQaWhEoMGmGslA==", + "cpu": [ + "x64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "freebsd" + ], "engines": { - "node": ">= 6" + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" } }, - "node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "node_modules/lightningcss-linux-arm-gnueabihf": { + "version": "1.30.2", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.30.2.tgz", + "integrity": "sha512-eVdpxh4wYcm0PofJIZVuYuLiqBIakQ9uFZmipf6LF/HRj5Bgm0eb3qL/mr1smyXIS1twwOxNWndd8z0E374hiA==", + "cpu": [ + "arm" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=8" + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" } }, - "node_modules/is-binary-path": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", - "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", - "dependencies": { - "binary-extensions": "^2.0.0" - }, + "node_modules/lightningcss-linux-arm64-gnu": { + "version": "1.30.2", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.30.2.tgz", + "integrity": "sha512-UK65WJAbwIJbiBFXpxrbTNArtfuznvxAJw4Q2ZGlU8kPeDIWEX1dg3rn2veBVUylA2Ezg89ktszWbaQnxD/e3A==", + "cpu": [ + "arm64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=8" + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" } }, - "node_modules/is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "node_modules/lightningcss-linux-arm64-musl": { + "version": "1.30.2", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.30.2.tgz", + "integrity": "sha512-5Vh9dGeblpTxWHpOx8iauV02popZDsCYMPIgiuw97OJ5uaDsL86cnqSFs5LZkG3ghHoX5isLgWzMs+eD1YzrnA==", + "cpu": [ + "arm64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=0.10.0" + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" } }, - "node_modules/is-glob": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "dependencies": { - "is-extglob": "^2.1.1" - }, + "node_modules/lightningcss-linux-x64-gnu": { + "version": "1.30.2", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.30.2.tgz", + "integrity": "sha512-Cfd46gdmj1vQ+lR6VRTTadNHu6ALuw2pKR9lYq4FnhvgBc4zWY1EtZcAc6EffShbb1MFrIPfLDXD6Xprbnni4w==", + "cpu": [ + "x64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=0.10.0" + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" } }, - "node_modules/is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "node_modules/lightningcss-linux-x64-musl": { + "version": "1.30.2", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.30.2.tgz", + "integrity": "sha512-XJaLUUFXb6/QG2lGIW6aIk6jKdtjtcffUT0NKvIqhSBY3hh9Ch+1LCeH80dR9q9LBjG3ewbDjnumefsLsP6aiA==", + "cpu": [ + "x64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=0.12.0" + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" } }, - "node_modules/jsonc-parser": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.2.1.tgz", - "integrity": "sha512-AilxAyFOAcK5wA1+LeaySVBrHsGQvUFCDWXKpZjzaL0PqW+xfBOttn8GNtWKFWqneyMZj41MWF9Kl6iPWLwgOA==" + "node_modules/lightningcss-win32-arm64-msvc": { + "version": "1.30.2", + "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.30.2.tgz", + "integrity": "sha512-FZn+vaj7zLv//D/192WFFVA0RgHawIcHqLX9xuWiQt7P0PtdFEVaxgF9rjM/IRYHQXNnk61/H/gb2Ei+kUQ4xQ==", + "cpu": [ + "arm64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } }, - "node_modules/leven": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", - "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", + "node_modules/lightningcss-win32-x64-msvc": { + "version": "1.30.2", + "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.30.2.tgz", + "integrity": "sha512-5g1yc73p+iAkid5phb4oVFMB45417DkRevRbt/El/gKXJk4jid+vPFF/AXbxn05Aky8PapwzZrdJShv5C0avjw==", + "cpu": [ + "x64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], "engines": { - "node": ">=6" + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" } }, "node_modules/lodash.camelcase": { @@ -291,6 +1275,18 @@ "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==" }, + "node_modules/loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "license": "MIT", + "dependencies": { + "js-tokens": "^3.0.0 || ^4.0.0" + }, + "bin": { + "loose-envify": "cli.js" + } + }, "node_modules/normalize-path": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", @@ -299,6 +1295,21 @@ "node": ">=0.10.0" } }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/pako": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/pako/-/pako-2.1.0.tgz", + "integrity": "sha512-w+eufiZ1WuJYgPXbV/PO3NCMEc3xqylkKHzp8bxp1uW4qaSNQUkwmLLEc3kKsfz8lpV1F8Ht3U1Cm+9Srog2ug==", + "license": "(MIT AND Zlib)" + }, "node_modules/picomatch": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", @@ -310,6 +1321,24 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "license": "MIT" + }, + "node_modules/prop-types": { + "version": "15.8.1", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", + "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", + "license": "MIT", + "peer": true, + "dependencies": { + "loose-envify": "^1.4.0", + "object-assign": "^4.1.1", + "react-is": "^16.13.1" + } + }, "node_modules/pyright-internal-browser": { "version": "1.1.367", "resolved": "https://registry.npmjs.org/pyright-internal-browser/-/pyright-internal-browser-1.1.367.tgz", @@ -354,6 +1383,91 @@ "vscode-uri": "^3.0.8" } }, + "node_modules/react": { + "version": "19.2.1", + "resolved": "https://registry.npmjs.org/react/-/react-19.2.1.tgz", + "integrity": "sha512-DGrYcCWK7tvYMnWh79yrPHt+vdx9tY+1gPZa7nJQtO/p8bLTDaHp4dzwEhQB7pZ4Xe3ok4XKuEPrVuc+wlpkmw==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-accessible-treeview": { + "version": "2.11.2", + "resolved": "https://registry.npmjs.org/react-accessible-treeview/-/react-accessible-treeview-2.11.2.tgz", + "integrity": "sha512-qui0g/gBDpP7VbtqelgJezAzAjKOY3IVi1Rq1NRJ7Z627RXKyKiQ4ooxLK2yauxTvNyU0ke9S0a2d9YUMbJJbA==", + "license": "MIT", + "peerDependencies": { + "classnames": "^2.2.6", + "prop-types": "^15.7.2", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", + "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, + "node_modules/react-data-grid": { + "version": "7.0.0-beta.59", + "resolved": "https://registry.npmjs.org/react-data-grid/-/react-data-grid-7.0.0-beta.59.tgz", + "integrity": "sha512-iAp/UYWjfmXYFsyKDtGDMP1IvhwtQSjCP6G/wFEbMNuumWGOEZF8Ut1S2Bp4XxVpOrBkEVKXn+QC3rs14AcB7A==", + "license": "MIT", + "peerDependencies": { + "react": "^19.2", + "react-dom": "^19.2" + } + }, + "node_modules/react-dom": { + "version": "19.2.1", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.1.tgz", + "integrity": "sha512-ibrK8llX2a4eOskq1mXKu/TGZj9qzomO+sNfO98M6d9zIPOEhlBkMkBUBLd1vgS0gQsLDBzA+8jJBVXDnfHmJg==", + "license": "MIT", + "peer": true, + "dependencies": { + "scheduler": "^0.27.0" + }, + "peerDependencies": { + "react": "^19.2.1" + } + }, + "node_modules/react-icons": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/react-icons/-/react-icons-4.12.0.tgz", + "integrity": "sha512-IBaDuHiShdZqmfc/TwHu6+d6k2ltNCf3AszxNmjJc1KUfXdEeRJOKyNvLmAHaarhzGmTSVygNdyu8/opXv2gaw==", + "license": "MIT", + "peerDependencies": { + "react": "*" + } + }, + "node_modules/react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", + "license": "MIT" + }, + "node_modules/react-resizable-panels": { + "version": "2.1.9", + "resolved": "https://registry.npmjs.org/react-resizable-panels/-/react-resizable-panels-2.1.9.tgz", + "integrity": "sha512-z77+X08YDIrgAes4jl8xhnUu1LNIRp4+E7cv4xHmLOxxUPO/ML7PSrE813b90vj7xvQ1lcf7g2uA9GeMZonjhQ==", + "license": "MIT", + "peerDependencies": { + "react": "^16.14.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc", + "react-dom": "^16.14.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" + } + }, + "node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, "node_modules/readdirp": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", @@ -365,6 +1479,33 @@ "node": ">=8.10.0" } }, + "node_modules/resolve-pkg-maps": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", + "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", + "license": "MIT", + "funding": { + "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" + } + }, + "node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "license": "MIT" + }, + "node_modules/scheduler": { + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz", + "integrity": "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==", + "license": "MIT" + }, + "node_modules/setimmediate": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", + "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==", + "license": "MIT" + }, "node_modules/source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", @@ -382,6 +1523,47 @@ "source-map": "^0.6.0" } }, + "node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/style-mod": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/style-mod/-/style-mod-4.1.3.tgz", + "integrity": "sha512-i/n8VsZydrugj3Iuzll8+x/00GH2vnYsk1eomD8QiRrSAeW6ItbCQDtfXCeJHd0iwiNagqjQkvpvREEPtW3IoQ==", + "license": "MIT" + }, "node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -418,6 +1600,25 @@ "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" }, + "node_modules/tsx": { + "version": "4.21.0", + "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.21.0.tgz", + "integrity": "sha512-5C1sg4USs1lfG0GFb2RLXsdpXqBSEhAaA/0kPL01wxzpMqLILNxIxIOKiILz+cdg/pLnOUxFYOR5yhHU666wbw==", + "license": "MIT", + "dependencies": { + "esbuild": "~0.27.0", + "get-tsconfig": "^4.7.5" + }, + "bin": { + "tsx": "dist/cli.mjs" + }, + "engines": { + "node": ">=18.0.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + } + }, "node_modules/typical": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/typical/-/typical-4.0.0.tgz", @@ -426,6 +1627,12 @@ "node": ">=8" } }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "license": "MIT" + }, "node_modules/vscode-jsonrpc": { "version": "9.0.0-next.4", "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-9.0.0-next.4.tgz", @@ -472,6 +1679,120 @@ "version": "3.0.8", "resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-3.0.8.tgz", "integrity": "sha512-AyFQ0EVmsOZOlAnxoFOGOq1SQDWAB7C6aqMGS23svWAllfOaxbuFvcT8D1i8z3Gyn8fraVeZNNmN6e9bxxXkKw==" + }, + "node_modules/w3c-keyname": { + "version": "2.2.8", + "resolved": "https://registry.npmjs.org/w3c-keyname/-/w3c-keyname-2.2.8.tgz", + "integrity": "sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ==", + "license": "MIT" + }, + "node_modules/webr": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/webr/-/webr-0.4.4.tgz", + "integrity": "sha512-hm1cKoa1S64LKQy/ZO7t7wK5hX7V5icFyVwPDB5tNk9KMDhYjQVdwupieal5B4qA2QCpNU9jj1F/CTvBB7MHYg==", + "license": "SEE LICENSE IN LICENCE.md", + "dependencies": { + "@codemirror/autocomplete": "^6.8.1", + "@codemirror/commands": "^6.2.4", + "@codemirror/state": "^6.2.1", + "@codemirror/view": "^6.15.0", + "@msgpack/msgpack": "^2.8.0", + "classnames": "^2.2.6", + "codemirror": "^6.0.1", + "codemirror-lang-r": "^0.1.0-2", + "jszip": "^3.10.1", + "lezer-r": "^0.1.1", + "lightningcss": "^1.21.5", + "pako": "^2.1.0", + "prop-types": "^15.7.2", + "react": "^18.2.0", + "react-accessible-treeview": "^2.6.1", + "react-data-grid": "^7.0.0-beta.44", + "react-dom": "^18.2.0", + "react-icons": "^4.10.1", + "react-resizable-panels": "^2.0.19", + "tsx": "^4.0.0", + "xmlhttprequest-ssl": "^2.1.0", + "xterm": "^5.1.0", + "xterm-addon-fit": "^0.7.0", + "xterm-readline": "^1.1.1" + }, + "engines": { + "node": ">=17.0.0" + } + }, + "node_modules/webr/node_modules/react": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", + "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "loose-envify": "^1.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/webr/node_modules/react-dom": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", + "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.1.0", + "scheduler": "^0.23.2" + }, + "peerDependencies": { + "react": "^18.3.1" + } + }, + "node_modules/webr/node_modules/scheduler": { + "version": "0.23.2", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz", + "integrity": "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.1.0" + } + }, + "node_modules/xmlhttprequest-ssl": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-2.1.2.tgz", + "integrity": "sha512-TEU+nJVUUnA4CYJFLvK5X9AOeH4KvDvhIfm0vV1GaQRtchnG0hgK5p8hw/xjv8cunWYCsiPCSDzObPyhEwq3KQ==", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/xterm": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/xterm/-/xterm-5.3.0.tgz", + "integrity": "sha512-8QqjlekLUFTrU6x7xck1MsPzPA571K5zNqWm0M0oroYEWVOptZ0+ubQSkQ3uxIEhcIHRujJy6emDWX4A7qyFzg==", + "deprecated": "This package is now deprecated. Move to @xterm/xterm instead.", + "license": "MIT", + "peer": true + }, + "node_modules/xterm-addon-fit": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/xterm-addon-fit/-/xterm-addon-fit-0.7.0.tgz", + "integrity": "sha512-tQgHGoHqRTgeROPnvmtEJywLKoC/V9eNs4bLLz7iyJr1aW/QFzRwfd3MGiJ6odJd9xEfxcW36/xRU47JkD5NKQ==", + "deprecated": "This package is now deprecated. Move to @xterm/addon-fit instead.", + "license": "MIT", + "peerDependencies": { + "xterm": "^5.0.0" + } + }, + "node_modules/xterm-readline": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/xterm-readline/-/xterm-readline-1.1.2.tgz", + "integrity": "sha512-1+W2nVuQvCYz9OUYwFBiolrSQUui51aDDyacKXt4PuxeBHqzvabQEJ2kwdBDzsmOjz5BwlDTAjJmYpH2OGqLFA==", + "license": "MIT", + "dependencies": { + "string-width": "^4" + }, + "peerDependencies": { + "@xterm/xterm": "^5.5.0" + } } } } diff --git a/server/package.json b/server/package.json index 3609e4fef..8907e59ed 100644 --- a/server/package.json +++ b/server/package.json @@ -11,6 +11,7 @@ "pyright-internal-node": "^1.1.367", "pyright-internal-browser": "^1.1.367", "vscode-languageserver": "^10.0.0-next.2", - "vscode-languageserver-textdocument": "1.0.11" + "vscode-languageserver-textdocument": "1.0.11", + "webr": "^0.4.2" } } \ No newline at end of file diff --git a/server/src/r/RLanguageProvider.ts b/server/src/r/RLanguageProvider.ts index 4e85418c8..779874ec9 100644 --- a/server/src/r/RLanguageProvider.ts +++ b/server/src/r/RLanguageProvider.ts @@ -5,7 +5,7 @@ import type { RLanguageProviderNode } from "./node/RLanguageProviderNode"; /** * Union type for R Language Provider implementations. - * + * * - Node: Full R language server support (spawns external R process) * - Browser: Stub implementation (R not available in WASM yet) */ diff --git a/server/src/r/browser/RLanguageProviderBrowser.ts b/server/src/r/browser/RLanguageProviderBrowser.ts new file mode 100644 index 000000000..1a208c652 --- /dev/null +++ b/server/src/r/browser/RLanguageProviderBrowser.ts @@ -0,0 +1,385 @@ +// Copyright © 2024, SAS Institute Inc., Cary, NC, USA. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +import { + CancellationToken, + CompletionItem, + CompletionItemKind, + CompletionList, + CompletionParams, + Connection, + Definition, + DefinitionLink, + DidCloseTextDocumentParams, + DidOpenTextDocumentParams, + Hover, + HoverParams, + InitializeParams, + InitializeResult, + MarkupContent, + MarkupKind, + SignatureHelp, + SignatureHelpParams, + TextDocumentPositionParams, +} from "vscode-languageserver"; +import { TextDocument } from "vscode-languageserver-textdocument"; + +import { WebR } from "webr"; + +import { LanguageServiceProvider } from "../../sas/LanguageServiceProvider"; +import { extractRCodes } from "../utils"; + +/** + * R Language Provider for Browser environment using WebR. + * + * WebR allows R to run directly in the browser via WebAssembly, + * providing basic language features like code completion and hover info. + */ +export class RLanguageProviderBrowser { + protected sasLspProvider?: (uri: string) => LanguageServiceProvider; + protected connection: Connection; + protected webR?: WebR; + protected rDocuments: Map = new Map(); + protected isInitialized = false; + + // Common R functions for autocomplete + private readonly commonRFunctions = [ + { label: "mean", detail: "mean(x, ...)", documentation: "Arithmetic Mean" }, + { + label: "median", + detail: "median(x, ...)", + documentation: "Median Value", + }, + { + label: "sum", + detail: "sum(...)", + documentation: "Sum of Vector Elements", + }, + { + label: "length", + detail: "length(x)", + documentation: "Length of an Object", + }, + { label: "print", detail: "print(x, ...)", documentation: "Print Values" }, + { + label: "cat", + detail: "cat(...)", + documentation: "Concatenate and Print", + }, + { + label: "c", + detail: "c(...)", + documentation: "Combine Values into a Vector", + }, + { label: "list", detail: "list(...)", documentation: "Create a List" }, + { + label: "data.frame", + detail: "data.frame(...)", + documentation: "Create a Data Frame", + }, + { + label: "matrix", + detail: "matrix(data, nrow, ncol)", + documentation: "Create a Matrix", + }, + { + label: "plot", + detail: "plot(x, y, ...)", + documentation: "Generic X-Y Plotting", + }, + { + label: "head", + detail: "head(x, n = 6)", + documentation: "Return First Parts of an Object", + }, + { + label: "tail", + detail: "tail(x, n = 6)", + documentation: "Return Last Parts of an Object", + }, + { + label: "str", + detail: "str(object, ...)", + documentation: "Display Structure of an Object", + }, + { + label: "summary", + detail: "summary(object, ...)", + documentation: "Object Summary", + }, + { + label: "lm", + detail: "lm(formula, data, ...)", + documentation: "Fit Linear Model", + }, + { + label: "glm", + detail: "glm(formula, family, data, ...)", + documentation: "Fit Generalized Linear Model", + }, + { + label: "read.csv", + detail: "read.csv(file, ...)", + documentation: "Read CSV File", + }, + { + label: "write.csv", + detail: "write.csv(x, file, ...)", + documentation: "Write CSV File", + }, + ]; + + constructor(connection: Connection) { + this.connection = connection; + } + + public setSasLspProvider( + provider: (uri: string) => LanguageServiceProvider, + ): void { + this.sasLspProvider = provider; + } + + public async initialize( + // eslint-disable-next-line @typescript-eslint/no-unused-vars + _params: InitializeParams, + ): Promise { + return { + capabilities: { + hoverProvider: true, + completionProvider: { + triggerCharacters: [".", "$"], + }, + }, + }; + } + + public onInitialized(): void { + // Initialize WebR asynchronously + this.initializeWebR().catch((err) => { + this.connection.console.error(`Failed to initialize WebR: ${err}`); + }); + } + + private async initializeWebR(): Promise { + try { + this.connection.console.log("Initializing WebR..."); + this.webR = new WebR(); + await this.webR.init(); + this.isInitialized = true; + this.connection.console.log("WebR initialized successfully"); + } catch (error) { + this.connection.console.error(`WebR initialization failed: ${error}`); + this.isInitialized = false; + } + } + + public addContentChange(doc: TextDocument): void { + if (!this.sasLspProvider) { + return; + } + + const lsp = this.sasLspProvider(doc.uri); + const rCode = extractRCodes(doc, lsp); + + if (rCode) { + this.rDocuments.set(doc.uri, rCode); + } else { + this.rDocuments.delete(doc.uri); + } + } + + public async onDidOpenTextDocument( + params: DidOpenTextDocumentParams, + ): Promise { + const doc = TextDocument.create( + params.textDocument.uri, + params.textDocument.languageId, + params.textDocument.version, + params.textDocument.text, + ); + this.addContentChange(doc); + } + + public async onDidCloseTextDocument( + params: DidCloseTextDocumentParams, + ): Promise { + this.rDocuments.delete(params.textDocument.uri); + } + + public async onHover( + params: HoverParams, + // eslint-disable-next-line @typescript-eslint/no-unused-vars + _token: CancellationToken, + ): Promise { + if (!this.isInitialized || !this.webR) { + return null; + } + + const doc = this.getDocument(params.textDocument.uri); + if (!doc) { + return null; + } + + // Get word at position + const word = this.getWordAtPosition( + doc, + params.position.line, + params.position.character, + ); + if (!word) { + return null; + } + + try { + // Try to get help for the function/object + const helpResult = await this.webR.evalRString( + `paste(capture.output(tryCatch(help('${word}'), error = function(e) '')), collapse = '\\n')`, + ); + + if (helpResult && helpResult.trim()) { + const contents: MarkupContent = { + kind: MarkupKind.Markdown, + value: `\`\`\`r\n${word}\n\`\`\`\n\n${helpResult.substring(0, 500)}...`, + }; + return { contents }; + } + + // Fallback: try to evaluate and show type + const typeResult = await this.webR.evalRString( + `tryCatch(class(${word}), error = function(e) '')`, + ); + + if (typeResult && typeResult.trim()) { + const contents: MarkupContent = { + kind: MarkupKind.Markdown, + value: `\`\`\`r\n${word}\n\`\`\`\n\nType: ${typeResult}`, + }; + return { contents }; + } + } catch { + // Silently fail - word might not be a valid R object + } + + return null; + } + + public async onCompletion( + // eslint-disable-next-line @typescript-eslint/no-unused-vars + _params: CompletionParams, + // eslint-disable-next-line @typescript-eslint/no-unused-vars + _token: CancellationToken, + ): Promise { + if (!this.isInitialized || !this.webR) { + return null; + } + + const items: CompletionItem[] = this.commonRFunctions.map((fn) => ({ + label: fn.label, + kind: CompletionItemKind.Function, + detail: fn.detail, + documentation: fn.documentation, + })); + + // Try to get installed packages for additional completions + try { + const packages = await this.webR.evalRString( + `paste(.packages(), collapse = ',')`, + ); + + if (packages) { + packages.split(",").forEach((pkg: string) => { + if (pkg.trim()) { + items.push({ + label: pkg.trim(), + kind: CompletionItemKind.Module, + detail: "Package", + }); + } + }); + } + } catch { + // Silently fail - completions still work with common functions + } + + return { + isIncomplete: false, + items, + }; + } + + public async onCompletionResolve( + item: CompletionItem, + // eslint-disable-next-line @typescript-eslint/no-unused-vars + _token: CancellationToken, + ): Promise { + return item; + } + + public async onSignatureHelp( + // eslint-disable-next-line @typescript-eslint/no-unused-vars + _params: SignatureHelpParams, + // eslint-disable-next-line @typescript-eslint/no-unused-vars + _token: CancellationToken, + ): Promise { + return null; + } + + public async onDefinition( + // eslint-disable-next-line @typescript-eslint/no-unused-vars + _params: TextDocumentPositionParams, + // eslint-disable-next-line @typescript-eslint/no-unused-vars + _token: CancellationToken, + ): Promise { + return null; + } + + public async onShutdown( + // eslint-disable-next-line @typescript-eslint/no-unused-vars + _token: CancellationToken, + ): Promise { + if (this.webR) { + try { + await this.webR.close(); + } catch (error) { + this.connection.console.error(`Error closing WebR: ${error}`); + } + } + this.rDocuments.clear(); + } + + private getDocument(uri: string): string | undefined { + return this.rDocuments.get(uri); + } + + private getWordAtPosition( + doc: string, + line: number, + character: number, + ): string | null { + const lines = doc.split("\n"); + if (line >= lines.length) { + return null; + } + + const lineText = lines[line]; + if (character >= lineText.length) { + return null; + } + + // Find word boundaries + let start = character; + let end = character; + + // Move start back to beginning of word + while (start > 0 && /[a-zA-Z0-9_.]/.test(lineText[start - 1])) { + start--; + } + + // Move end forward to end of word + while (end < lineText.length && /[a-zA-Z0-9_.]/.test(lineText[end])) { + end++; + } + + const word = lineText.substring(start, end); + return word.length > 0 ? word : null; + } +} diff --git a/server/src/r/node/RLanguageProviderNode.ts b/server/src/r/node/RLanguageProviderNode.ts new file mode 100644 index 000000000..5784be7c4 --- /dev/null +++ b/server/src/r/node/RLanguageProviderNode.ts @@ -0,0 +1,531 @@ +// Copyright © 2024, SAS Institute Inc., Cary, NC, USA. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +import { + CancellationToken, + CompletionItem, + CompletionItemKind, + CompletionList, + CompletionParams, + Connection, + Definition, + DefinitionLink, + DidCloseTextDocumentParams, + DidOpenTextDocumentParams, + Hover, + HoverParams, + InitializeParams, + InitializeResult, + MarkupContent, + MarkupKind, + Position, + SignatureHelp, + SignatureHelpParams, + TextDocumentPositionParams, +} from "vscode-languageserver"; +import { TextDocument } from "vscode-languageserver-textdocument"; + +import { CodeZoneManager } from "../../sas/CodeZoneManager"; +import { LanguageServiceProvider } from "../../sas/LanguageServiceProvider"; +import { extractRCodes } from "../utils"; + +interface RFunctionInfo { + label: string; + detail: string; + documentation: string; + signature?: string; +} + +/** + * R Language Provider for Node environment. + * + * Provides basic R language features like code completion and hover info + * using static function definitions. No R installation required. + */ +export class RLanguageProviderNode { + protected sasLspProvider?: (uri: string) => LanguageServiceProvider; + protected connection: Connection; + protected rDocuments: Map = new Map(); + + // Common R functions with documentation + private readonly rFunctions: Map = new Map([ + [ + "mean", + { + label: "mean", + detail: "mean(x, trim = 0, na.rm = FALSE, ...)", + documentation: + "**Arithmetic Mean**\n\nCompute the arithmetic mean of a numeric vector.\n\n**Arguments:**\n- `x`: An R object\n- `trim`: Fraction of observations to be trimmed\n- `na.rm`: Logical. Should missing values be removed?", + signature: "mean(x, trim = 0, na.rm = FALSE, ...)", + }, + ], + [ + "median", + { + label: "median", + detail: "median(x, na.rm = FALSE, ...)", + documentation: + "**Median Value**\n\nCompute the sample median.\n\n**Arguments:**\n- `x`: An R object\n- `na.rm`: Logical. Should missing values be removed?", + signature: "median(x, na.rm = FALSE, ...)", + }, + ], + [ + "sum", + { + label: "sum", + detail: "sum(..., na.rm = FALSE)", + documentation: + "**Sum of Vector Elements**\n\nReturns the sum of all the values present in its arguments.\n\n**Arguments:**\n- `...`: Numeric or complex or logical vectors\n- `na.rm`: Logical. Should missing values be removed?", + signature: "sum(..., na.rm = FALSE)", + }, + ], + [ + "length", + { + label: "length", + detail: "length(x)", + documentation: + "**Length of an Object**\n\nGet or set the length of vectors (including lists) and factors.\n\n**Arguments:**\n- `x`: An R object", + signature: "length(x)", + }, + ], + [ + "print", + { + label: "print", + detail: "print(x, ...)", + documentation: + "**Print Values**\n\nPrints its argument and returns it invisibly.\n\n**Arguments:**\n- `x`: An R object\n- `...`: Further arguments passed to methods", + signature: "print(x, ...)", + }, + ], + [ + "cat", + { + label: "cat", + detail: "cat(..., sep = ' ')", + documentation: + "**Concatenate and Print**\n\nOutputs the objects, concatenating the representations.\n\n**Arguments:**\n- `...`: R objects\n- `sep`: Character string to separate arguments", + signature: "cat(..., sep = ' ')", + }, + ], + [ + "c", + { + label: "c", + detail: "c(...)", + documentation: + "**Combine Values into a Vector**\n\nCombine Values into a Vector or List.\n\n**Arguments:**\n- `...`: Objects to be concatenated", + signature: "c(...)", + }, + ], + [ + "list", + { + label: "list", + detail: "list(...)", + documentation: + "**Create a List**\n\nFunctions to construct, coerce and check for both kinds of R lists.\n\n**Arguments:**\n- `...`: Objects, possibly named", + signature: "list(...)", + }, + ], + [ + "data.frame", + { + label: "data.frame", + detail: "data.frame(..., row.names = NULL)", + documentation: + "**Create a Data Frame**\n\nCreates data frames, tightly coupled collections of variables.\n\n**Arguments:**\n- `...`: Column vectors\n- `row.names`: NULL or character vector", + signature: "data.frame(..., row.names = NULL)", + }, + ], + [ + "matrix", + { + label: "matrix", + detail: "matrix(data = NA, nrow = 1, ncol = 1)", + documentation: + "**Create a Matrix**\n\nCreates a matrix from the given set of values.\n\n**Arguments:**\n- `data`: Data vector\n- `nrow`: Number of rows\n- `ncol`: Number of columns", + signature: "matrix(data = NA, nrow = 1, ncol = 1)", + }, + ], + [ + "plot", + { + label: "plot", + detail: "plot(x, y, ...)", + documentation: + "**Generic X-Y Plotting**\n\nGeneric function for plotting of R objects.\n\n**Arguments:**\n- `x`: X coordinates\n- `y`: Y coordinates\n- `...`: Graphical parameters", + signature: "plot(x, y, ...)", + }, + ], + [ + "head", + { + label: "head", + detail: "head(x, n = 6L)", + documentation: + "**Return First Parts of an Object**\n\nReturns the first parts of a vector, matrix, table, data frame or function.\n\n**Arguments:**\n- `x`: An object\n- `n`: Integer. Number of elements to extract", + signature: "head(x, n = 6L)", + }, + ], + [ + "tail", + { + label: "tail", + detail: "tail(x, n = 6L)", + documentation: + "**Return Last Parts of an Object**\n\nReturns the last parts of a vector, matrix, table, data frame or function.\n\n**Arguments:**\n- `x`: An object\n- `n`: Integer. Number of elements to extract", + signature: "tail(x, n = 6L)", + }, + ], + [ + "str", + { + label: "str", + detail: "str(object, ...)", + documentation: + "**Display Structure of an Object**\n\nCompactly display the internal structure of an R object.\n\n**Arguments:**\n- `object`: Any R object\n- `...`: Additional arguments", + signature: "str(object, ...)", + }, + ], + [ + "summary", + { + label: "summary", + detail: "summary(object, ...)", + documentation: + "**Object Summary**\n\nGeneric function used to produce result summaries.\n\n**Arguments:**\n- `object`: An object\n- `...`: Additional arguments", + signature: "summary(object, ...)", + }, + ], + [ + "lm", + { + label: "lm", + detail: "lm(formula, data, ...)", + documentation: + "**Fit Linear Model**\n\nFit linear models. Used to fit linear regression.\n\n**Arguments:**\n- `formula`: Model formula\n- `data`: Data frame\n- `...`: Additional arguments", + signature: "lm(formula, data, ...)", + }, + ], + [ + "glm", + { + label: "glm", + detail: "glm(formula, family, data, ...)", + documentation: + "**Fit Generalized Linear Model**\n\nFit generalized linear models.\n\n**Arguments:**\n- `formula`: Model formula\n- `family`: Error distribution\n- `data`: Data frame", + signature: "glm(formula, family, data, ...)", + }, + ], + [ + "read.csv", + { + label: "read.csv", + detail: "read.csv(file, header = TRUE, ...)", + documentation: + "**Read CSV File**\n\nReads a file in table format and creates a data frame.\n\n**Arguments:**\n- `file`: File path\n- `header`: Logical. Does file have header?\n- `...`: Additional arguments", + signature: "read.csv(file, header = TRUE, ...)", + }, + ], + [ + "write.csv", + { + label: "write.csv", + detail: "write.csv(x, file, ...)", + documentation: + "**Write CSV File**\n\nWrites a data frame to a CSV file.\n\n**Arguments:**\n- `x`: Data frame to write\n- `file`: Output file path\n- `...`: Additional arguments", + signature: "write.csv(x, file, ...)", + }, + ], + [ + "paste", + { + label: "paste", + detail: "paste(..., sep = ' ', collapse = NULL)", + documentation: + "**Concatenate Strings**\n\nConcatenate vectors after converting to character.\n\n**Arguments:**\n- `...`: One or more R objects\n- `sep`: Character string to separate terms\n- `collapse`: Optional character string to separate results", + signature: "paste(..., sep = ' ', collapse = NULL)", + }, + ], + [ + "for", + { + label: "for", + detail: "for (var in seq) expr", + documentation: + "**For Loop**\n\nExecutes a loop over a sequence.\n\n**Arguments:**\n- `var`: Loop variable\n- `seq`: Sequence to iterate over\n- `expr`: Expression to execute", + signature: "for (var in seq) expr", + }, + ], + ]); + + constructor(connection: Connection) { + this.connection = connection; + } + + public setSasLspProvider( + provider: (uri: string) => LanguageServiceProvider, + ): void { + this.sasLspProvider = provider; + } + + public async initialize( + // eslint-disable-next-line @typescript-eslint/no-unused-vars + _params: InitializeParams, + ): Promise { + return { + capabilities: { + hoverProvider: true, + completionProvider: { + triggerCharacters: [".", "$"], + }, + }, + }; + } + + public onInitialized(): void { + this.connection.console.log( + "R language provider initialized (static mode)", + ); + } + + public addContentChange(doc: TextDocument): void { + if (!this.sasLspProvider) { + return; + } + + const lsp = this.sasLspProvider(doc.uri); + const rCode = extractRCodes(doc, lsp); + + if (rCode) { + this.rDocuments.set(doc.uri, rCode); + } else { + this.rDocuments.delete(doc.uri); + } + } + + public async onDidOpenTextDocument( + params: DidOpenTextDocumentParams, + ): Promise { + const doc = TextDocument.create( + params.textDocument.uri, + params.textDocument.languageId, + params.textDocument.version, + params.textDocument.text, + ); + this.addContentChange(doc); + } + + public async onDidCloseTextDocument( + params: DidCloseTextDocumentParams, + ): Promise { + this.rDocuments.delete(params.textDocument.uri); + } + + public async onHover( + params: HoverParams, + // eslint-disable-next-line @typescript-eslint/no-unused-vars + _token: CancellationToken, + ): Promise { + const doc = this.getDocument(params.textDocument.uri); + if (!doc) { + return null; + } + + // Map SAS position to R code position + const rPosition = this.mapSasPositionToR( + params.textDocument.uri, + params.position, + ); + if (!rPosition) { + return null; + } + + // Get word at position in R code + const word = this.getWordAtPosition( + doc, + rPosition.line, + rPosition.character, + ); + if (!word) { + return null; + } + + // Look up function info + const funcInfo = this.rFunctions.get(word); + if (funcInfo) { + const contents: MarkupContent = { + kind: MarkupKind.Markdown, + value: `\`\`\`r\n${funcInfo.detail}\n\`\`\`\n\n${funcInfo.documentation}`, + }; + return { contents }; + } + + return null; + } + + public async onCompletion( + // eslint-disable-next-line @typescript-eslint/no-unused-vars + _params: CompletionParams, + // eslint-disable-next-line @typescript-eslint/no-unused-vars + _token: CancellationToken, + ): Promise { + const items: CompletionItem[] = Array.from(this.rFunctions.values()).map( + (fn) => ({ + label: fn.label, + kind: CompletionItemKind.Function, + detail: fn.detail, + documentation: fn.documentation, + }), + ); + + return { + isIncomplete: false, + items, + }; + } + + public async onCompletionResolve( + item: CompletionItem, + // eslint-disable-next-line @typescript-eslint/no-unused-vars + _token: CancellationToken, + ): Promise { + return item; + } + + public async onSignatureHelp( + // eslint-disable-next-line @typescript-eslint/no-unused-vars + _params: SignatureHelpParams, + // eslint-disable-next-line @typescript-eslint/no-unused-vars + _token: CancellationToken, + ): Promise { + return null; + } + + public async onDefinition( + // eslint-disable-next-line @typescript-eslint/no-unused-vars + _params: TextDocumentPositionParams, + // eslint-disable-next-line @typescript-eslint/no-unused-vars + _token: CancellationToken, + ): Promise { + return null; + } + + public async onShutdown( + // eslint-disable-next-line @typescript-eslint/no-unused-vars + _token: CancellationToken, + ): Promise { + this.rDocuments.clear(); + } + + private getDocument(uri: string): string | undefined { + return this.rDocuments.get(uri); + } + + private mapSasPositionToR( + sasUri: string, + sasPosition: Position, + ): Position | null { + if (!this.sasLspProvider) { + return null; + } + + const lsp = this.sasLspProvider(sasUri); + const codeZoneManager = lsp.getCodeZoneManager(); + const zone = codeZoneManager.getCurrentZone( + sasPosition.line, + sasPosition.character, + ); + + // Only process if we're in an embedded language zone (R code) + if (zone !== CodeZoneManager.ZONE_TYPE.EMBEDDED_LANG) { + return null; + } + + // Find the line offset in the extracted R code + const symbols = lsp.getDocumentSymbols(); + + let rLineOffset = 0; + for (const symbol of symbols) { + if (symbol.name?.toUpperCase() !== "PROC RLANG") { + continue; + } + + // Find the start of the R code within this PROC RLANG + let rCodeStartLine = symbol.range.start.line; + const pos = { line: symbol.range.start.line, character: 0 }; + + while (pos.line <= symbol.range.end.line) { + const currentZone = codeZoneManager.getCurrentZone( + pos.line, + pos.character, + ); + if (currentZone === CodeZoneManager.ZONE_TYPE.EMBEDDED_LANG) { + rCodeStartLine = pos.line; + break; + } + pos.line++; + } + + // If the cursor is in this PROC RLANG block + if ( + sasPosition.line >= rCodeStartLine && + sasPosition.line <= symbol.range.end.line + ) { + const rLine = sasPosition.line - rCodeStartLine + rLineOffset; + return { + line: rLine, + character: sasPosition.character, + }; + } + + // Count lines in this R block for offset calculation + let blockLines = 0; + for (let line = rCodeStartLine; line <= symbol.range.end.line; line++) { + if ( + codeZoneManager.getCurrentZone(line, 0) === + CodeZoneManager.ZONE_TYPE.EMBEDDED_LANG + ) { + blockLines++; + } + } + rLineOffset += blockLines; + } + + return null; + } + + private getWordAtPosition( + doc: string, + line: number, + character: number, + ): string | null { + const lines = doc.split("\n"); + if (line >= lines.length) { + return null; + } + + const lineText = lines[line]; + if (character >= lineText.length) { + return null; + } + + // Find word boundaries + let start = character; + let end = character; + + // Move start back to beginning of word + while (start > 0 && /[a-zA-Z0-9_.]/.test(lineText[start - 1])) { + start--; + } + + // Move end forward to end of word + while (end < lineText.length && /[a-zA-Z0-9_.]/.test(lineText[end])) { + end++; + } + + const word = lineText.substring(start, end); + return word.length > 0 ? word : null; + } +} diff --git a/server/test/python/extract_python_codes.test.ts b/server/test/python/extract_python_codes.test.ts index f6f036d71..6eae8ada7 100644 --- a/server/test/python/extract_python_codes.test.ts +++ b/server/test/python/extract_python_codes.test.ts @@ -4,8 +4,8 @@ import { TextDocument } from "vscode-languageserver-textdocument"; import { assert } from "chai"; -import { LanguageServiceProvider } from "../../src/sas/LanguageServiceProvider"; import { extractPythonCodes } from "../../src/python/utils"; +import { LanguageServiceProvider } from "../../src/sas/LanguageServiceProvider"; describe("Python Code Extraction", () => { it("extracts simple Python code from PROC PYTHON", () => { diff --git a/syntaxes/sas.tmLanguage.json b/syntaxes/sas.tmLanguage.json index 05616611b..eada48c42 100644 --- a/syntaxes/sas.tmLanguage.json +++ b/syntaxes/sas.tmLanguage.json @@ -76,6 +76,31 @@ } ] }, + { + "begin": "(?i)proc(\\s|/\\*.*?\\*/)*rlang", + "end": "(?i)(?=(run|quit)(\\s|/\\*.*?\\*/)*;|(data|proc|%macro)\\b[^;]*;)", + "name": "sas.proc.rlang", + "patterns": [ + { + "include": "#strings-or-comments" + }, + { + "begin": "(?i)(?<=\\bsubmit|\\binteractive|\\bi)(\\s|/\\*.*?\\*/)*;", + "end": "(?i)(endsubmit|endinteractive)(\\s|/\\*.*?\\*/)*;", + "name": "source.r", + "beginCaptures": { + "0": { + "name": "strange_bug" + } + }, + "patterns": [ + { + "include": "source.r" + } + ] + } + ] + }, { "begin": "(?i)data\\b.*?;", "end": "(?i)(?=(run|quit)(\\s|/\\*.*?\\*/)*;|(data|proc|%macro)\\b[^;]*;)", From 759b1d4d5aa95674f184d691aabd4c5ae20664ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elijah=20C=C3=BAchulainn=20Reid?= <56865341+Wizzzzzzard@users.noreply.github.com> Date: Mon, 8 Dec 2025 14:52:16 +0000 Subject: [PATCH 31/34] Wire R language providers into SAS LSP server MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Elijah Cúchulainn Reid <56865341+Wizzzzzzard@users.noreply.github.com> --- server/src/browser/server.ts | 7 ++- server/src/node/server.ts | 7 ++- server/src/server.ts | 103 ++++++++++++++++++++++++++++++++++- 3 files changed, 114 insertions(+), 3 deletions(-) diff --git a/server/src/browser/server.ts b/server/src/browser/server.ts index 82f4a96ac..6ca8156cc 100644 --- a/server/src/browser/server.ts +++ b/server/src/browser/server.ts @@ -8,6 +8,7 @@ import { } from "vscode-languageserver/browser"; import { PyrightLanguageProviderBrowser } from "../python/browser/PyrightLanguageProviderBrowser"; +import { RLanguageProviderBrowser } from "../r/browser/RLanguageProviderBrowser"; import { runServer } from "../server"; /* browser specific setup code */ @@ -16,4 +17,8 @@ const messageWriter = new BrowserMessageWriter(self); const connection: Connection = createConnection(messageReader, messageWriter); -runServer(connection, new PyrightLanguageProviderBrowser(connection, 1)); +runServer( + connection, + new PyrightLanguageProviderBrowser(connection, 1), + new RLanguageProviderBrowser(connection), +); diff --git a/server/src/node/server.ts b/server/src/node/server.ts index c01771b0c..e9992df8a 100644 --- a/server/src/node/server.ts +++ b/server/src/node/server.ts @@ -4,8 +4,13 @@ import { Connection } from "vscode-languageserver"; import { ProposedFeatures, createConnection } from "vscode-languageserver/node"; import { PyrightLanguageProviderNode } from "../python/node/PyrightLanguageProviderNode"; +import { RLanguageProviderNode } from "../r/node/RLanguageProviderNode"; import { runServer } from "../server"; const connection: Connection = createConnection(ProposedFeatures.all); -runServer(connection, new PyrightLanguageProviderNode(connection, 1)); +runServer( + connection, + new PyrightLanguageProviderNode(connection, 1), + new RLanguageProviderNode(connection), +); diff --git a/server/src/server.ts b/server/src/server.ts index 3c8ca1963..82a5405c8 100644 --- a/server/src/server.ts +++ b/server/src/server.ts @@ -38,6 +38,7 @@ import { CollectionResult } from "pyright-internal-node/dist/packages/pyright-in import { ParseFileResults } from "pyright-internal-node/dist/packages/pyright-internal/src/parser/parser"; import { PyrightLanguageProvider } from "./python/PyrightLanguageProvider"; +import { RLanguageProvider } from "./r/RLanguageProvider"; import { CodeZoneManager } from "./sas/CodeZoneManager"; import { LanguageServiceProvider, legend } from "./sas/LanguageServiceProvider"; import type { LibCompleteItem } from "./sas/SyntaxDataProvider"; @@ -52,6 +53,7 @@ interface DocumentInfo { export const runServer = ( connection: Connection, _pyrightLanguageProvider: PyrightLanguageProvider, + _rLanguageProvider: RLanguageProvider, ) => { const documentPool: Record = {}; @@ -59,6 +61,7 @@ export const runServer = ( let registeredAdvancedCapabilities = false; _pyrightLanguageProvider.setSasLspProvider(getLanguageService); + _rLanguageProvider.setSasLspProvider(getLanguageService); connection.onInitialize((params) => { if ( @@ -117,7 +120,10 @@ export const runServer = ( return result; }); - connection.onInitialized(() => _pyrightLanguageProvider.onInitialized()); + connection.onInitialized(() => { + _pyrightLanguageProvider.onInitialized(); + _rLanguageProvider.onInitialized(); + }); connection.onRequest(SemanticTokensRequest.type, (params) => { syncIfDocChange(params.textDocument.uri); @@ -136,6 +142,9 @@ export const runServer = ( async python(pyrightLanguageService) { return await pyrightLanguageService.onHover(params, token); }, + async r(rLanguageService) { + return await rLanguageService.onHover(params, token); + }, }); }); @@ -216,6 +225,53 @@ export const runServer = ( } return completionList; }, + async r(rLanguageService) { + const completionList = await rLanguageService.onCompletion( + params, + token, + ); + if (completionList) { + for (const item of completionList.items) { + if (!item.data) { + item.data = {}; + } + item.data._languageService = "r"; + item.data._uri = params.textDocument.uri; + } + + if ( + params.context?.triggerKind === CompletionTriggerKind.Invoked || + params.context?.triggerKind === + CompletionTriggerKind.TriggerForIncompleteCompletions + ) { + const doc = documentPool[params.textDocument.uri].document; + const line = doc.getText({ + start: { + line: params.position.line, + character: 0, + }, + end: params.position, + }); + if (!/\W/.test(line.trimStart())) { + const item = { + kind: CompletionItemKind.Keyword, + data: { + _languageService: "sas", + _uri: params.textDocument.uri, + }, + }; + if ( + completionList.items.findIndex( + (item) => item.label === "endsubmit", + ) === -1 + ) { + completionList.items.push({ ...item, label: "endsubmit" }); + } + } + } + } + return completionList; + }, }); }); @@ -231,6 +287,11 @@ export const runServer = ( completionItem, token, ); + } else if (lang === "r") { + return await _rLanguageProvider.onCompletionResolve( + completionItem, + token, + ); } return completionItem; }); @@ -329,6 +390,14 @@ export const runServer = ( // eslint-disable-next-line @typescript-eslint/no-explicit-any )) as any; }, + async r(rLanguageService) { + // eslint-disable-next-line @typescript-eslint/consistent-type-assertions + return (await rLanguageService.onSignatureHelp( + params, + token, + // eslint-disable-next-line @typescript-eslint/no-explicit-any + )) as any; + }, }); }); @@ -350,12 +419,14 @@ export const runServer = ( ); documentPool[doc.uri] = { document: doc, changed: false }; await _pyrightLanguageProvider.onDidOpenTextDocument(params); + await _rLanguageProvider.onDidOpenTextDocument(params); }); connection.onDidCloseTextDocument(async (params) => { const uri = params.textDocument.uri; delete documentPool[uri]; await _pyrightLanguageProvider.onDidCloseTextDocument(params); + await _rLanguageProvider.onDidCloseTextDocument(params); }); connection.onDidChangeTextDocument((params) => { @@ -382,6 +453,9 @@ export const runServer = ( async python(pyrightLanguageService) { return await pyrightLanguageService.onDefinition(params, token); }, + async r(rLanguageService) { + return await rLanguageService.onDefinition(params, token); + }, }); }, ); @@ -562,6 +636,7 @@ export const runServer = ( connection.onShutdown(async (token) => { await _pyrightLanguageProvider.onShutdown(token); + await _rLanguageProvider.onShutdown(token); }); // Listen on the connection @@ -591,9 +666,11 @@ export const runServer = ( python?: ( pyrightLanguageService: PyrightLanguageProvider, ) => Promise; + r?: (rLanguageService: RLanguageProvider) => Promise; default?: (languageServices: { sasLanguageService: LanguageServiceProvider; pythonLanguageService?: PyrightLanguageProvider; + rLanguageService?: RLanguageProvider; }) => Promise; }, ) => { @@ -627,6 +704,24 @@ export const runServer = ( return await callbacks.default({ sasLanguageService: languageService, pythonLanguageService: _pyrightLanguageProvider, + rLanguageService: _rLanguageProvider, + }); + } else { + return undefined; + } + } + if ( + symbol.name?.toUpperCase() === "PROC RLANG" && + codeZoneManager.getCurrentZone(pos.line, pos.character) === + CodeZoneManager.ZONE_TYPE.EMBEDDED_LANG + ) { + if (callbacks.r) { + return await callbacks.r(_rLanguageProvider); + } else if (callbacks.default) { + return await callbacks.default({ + sasLanguageService: languageService, + pythonLanguageService: _pyrightLanguageProvider, + rLanguageService: _rLanguageProvider, }); } else { return undefined; @@ -640,6 +735,7 @@ export const runServer = ( return await callbacks.default({ sasLanguageService: languageService, pythonLanguageService: _pyrightLanguageProvider, + rLanguageService: _rLanguageProvider, }); } { @@ -660,11 +756,16 @@ export const runServer = ( const syncIfDocChange = (uri: string) => { const docInfo = documentPool[uri]; + if (!docInfo) { + // Document not in pool yet - this can happen if hover is triggered before onDidOpenTextDocument + return; + } if (!docInfo.changed) { return; } docInfo.changed = false; _pyrightLanguageProvider.addContentChange(docInfo.document); + _rLanguageProvider.addContentChange(docInfo.document); }; const syncAllChangedDoc = () => { From 43ce6263c7128d95e7b0a4d7432147eefa9b71b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elijah=20C=C3=BAchulainn=20Reid?= <56865341+Wizzzzzzard@users.noreply.github.com> Date: Mon, 8 Dec 2025 14:55:48 +0000 Subject: [PATCH 32/34] Add PROC RLANG test fixture MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Elijah Cúchulainn Reid <56865341+Wizzzzzzard@users.noreply.github.com> --- server/testFixture/embedded_lang/proc_rlang.sas | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/server/testFixture/embedded_lang/proc_rlang.sas b/server/testFixture/embedded_lang/proc_rlang.sas index da286d1e4..e608f2c28 100644 --- a/server/testFixture/embedded_lang/proc_rlang.sas +++ b/server/testFixture/embedded_lang/proc_rlang.sas @@ -2,7 +2,8 @@ proc rlang; submit; for (x in 1:6) { print(x) + mean(x) } - print("first statement after for loop") + paste("first statement after for loop") endsubmit; -run; \ No newline at end of file +run; From 18fbd541e1eaebdee54377e58a284737464afec7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elijah=20C=C3=BAchulainn=20Reid?= <56865341+Wizzzzzzard@users.noreply.github.com> Date: Mon, 8 Dec 2025 14:59:23 +0000 Subject: [PATCH 33/34] Document R language support features MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Elijah Cúchulainn Reid <56865341+Wizzzzzzard@users.noreply.github.com> --- README.md | 13 ++++++++++- website/docs/Features/sasNotebook.md | 17 ++++++++++++++ website/docs/faq.md | 33 ++++++++++++++++++++++++++++ 3 files changed, 62 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index ba6de89fd..4dbe22f75 100644 --- a/README.md +++ b/README.md @@ -10,10 +10,21 @@ The SAS extension includes many [features](https://sassoftware.github.io/vscode- - SAS syntax highlighting and help, code completion, and code snippets - Navigate SAS Content and libraries, including table viewer -- Create notebooks for SAS, SQL, Python, R, and other languages +- Create notebooks for SAS, SQL, Python, R, Julia, and other languages +## Language Support + +The SAS extension provides enhanced language support for embedded languages in SAS notebooks and code files: + +- **Python**: Full IntelliSense support (code completion, hover, signature help) is included via Pyright +- **R**: + - **In VS Code Desktop**: Basic IntelliSense support (code completion, hover) for common R functions - no installation required + - **In Browser (VS Code for Web)**: Basic IntelliSense support provided automatically via WebR (no installation required) +- **Julia**: Syntax highlighting and execution via PROC JULIA +- **SQL** and **Lua**: Syntax highlighting and execution support + ## Support ### SAS Communities diff --git a/website/docs/Features/sasNotebook.md b/website/docs/Features/sasNotebook.md index 01066361d..69e7334da 100644 --- a/website/docs/Features/sasNotebook.md +++ b/website/docs/Features/sasNotebook.md @@ -19,6 +19,23 @@ Starting with Visual Studio Code version 1.93, [the language for SQL files has b ::: +## Language Support for Embedded Code + +SAS Notebook supports multiple languages including Python, R, SQL, and Lua. When working with these languages in SAS notebooks, enhanced language features such as code completion, hover information, and signature help are available. + +### Python Language Features + +Python code cells benefit from integrated IntelliSense powered by Pyright, which is included with the extension. No additional setup is required for Python language features. + +### R Language Features + +R code cells include basic IntelliSense features (code completion and hover documentation) for common R functions: + +- **In VS Code Desktop**: Basic code completion and hover information for common R functions is provided automatically - no installation required +- **In Browser (VS Code for Web)**: Enhanced R language features are automatically available via WebR - no installation required! WebR provides basic code completion and hover information for common R functions. + +Note: R code execution via PROC RLANG requires a SAS connection, but the language features work independently. + ## Export To export your SAS Notebook to other formats, click the **More Actions** (`...`) button on the notebook toolbar at top, and select `Export`. The following formats are supported. diff --git a/website/docs/faq.md b/website/docs/faq.md index 890425d00..e91bca559 100644 --- a/website/docs/faq.md +++ b/website/docs/faq.md @@ -107,3 +107,36 @@ If the options on the Problems panel toolbar are not visible, you can display th ### Can I control whether errors and warnings from my SAS log are displayed in the Problems panel? Yes. The `SAS.problems.log` setting controls whether problems from the SAS log are displayed in the Problems panel. This option is enabled by default. To access this option, select `File > Preferences > Settings`, and search for "sas problems". + +## Language Server questions + +### Why don't I see code completion or hover information in my PROC RLANG code? + +**In VS Code Desktop**: Basic R language features (code completion and hover) are provided automatically for common R functions - no installation required. If you're not seeing them: + +1. Make sure you're inside a `PROC RLANG` submit block +2. Check that your cursor is positioned on R code (not SAS code) +3. Try restarting VS Code if features don't appear + +**In Browser (VS Code for Web)**: R language features are automatically available via WebR. If you don't see completions: + +```r +1. WebR may still be initializing - wait a few moments after opening the file +2. Check the console log (`Help > Toggle Developer Tools`) for WebR initialization messages +3. Refresh the page if features don\'t appear after waiting + +### How can I verify that R language features are working? + +**In VS Code Desktop**: +1. Open a SAS file with a PROC RLANG submit block +2. Type common R functions like `mean`, `sum`, `print`, or `paste` inside the submit block +3. You should see autocomplete suggestions as you type +4. Hover over these function names - you should see documentation + +If features don\'t work, check the VS Code console log (`Help > Toggle Developer Tools`) for errors. + +**In Browser (VS Code for Web)**: +1. Open a SAS file with a PROC RLANG block +2. Check the console log (`Help > Toggle Developer Tools`) for messages like "WebR initialized successfully" +3. Try typing common R functions like `mean`, `sum`, or `plot` - you should see autocomplete suggestions +``` From ed369f455448d7fa1510aaa8b18920d7810ddd1f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elijah=20C=C3=BAchulainn=20Reid?= <56865341+Wizzzzzzard@users.noreply.github.com> Date: Mon, 8 Dec 2025 14:59:37 +0000 Subject: [PATCH 34/34] Spacing fixes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Elijah Cúchulainn Reid <56865341+Wizzzzzzard@users.noreply.github.com> --- README.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 4dbe22f75..d514502b9 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ The SAS extension includes many [features](https://sassoftware.github.io/vscode- - SAS syntax highlighting and help, code completion, and code snippets - Navigate SAS Content and libraries, including table viewer -- Create notebooks for SAS, SQL, Python, R, Julia, and other languages +- Create notebooks for SAS, SQL, Python, R, and other languages @@ -19,10 +19,9 @@ The SAS extension includes many [features](https://sassoftware.github.io/vscode- The SAS extension provides enhanced language support for embedded languages in SAS notebooks and code files: - **Python**: Full IntelliSense support (code completion, hover, signature help) is included via Pyright -- **R**: +- **R**: - **In VS Code Desktop**: Basic IntelliSense support (code completion, hover) for common R functions - no installation required - **In Browser (VS Code for Web)**: Basic IntelliSense support provided automatically via WebR (no installation required) -- **Julia**: Syntax highlighting and execution via PROC JULIA - **SQL** and **Lua**: Syntax highlighting and execution support ## Support