diff --git a/CHANGELOG.md b/CHANGELOG.md index 0474a5238..ac1a25e15 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,27 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). If you introduce breaking changes, please group them together in the "Changed" section using the **BREAKING:** prefix. +## [v1.13.0] - 2024-12-23 + +### Changed + +- Required VS Code version 1.89 at minimum + +### Added + +- Python language features inside proc python ([#991](https://github.com/sassoftware/vscode-sas-extension/pull/991)) +- Inherit VS Code file icons in SAS Content and Server ([#1310](https://github.com/sassoftware/vscode-sas-extension/pull/1310)) +- Display macro name in outline pane ([#1326](https://github.com/sassoftware/vscode-sas-extension/pull/1326)) + +### Fixed + +- Display global option help in data step ([#1282](https://github.com/sassoftware/vscode-sas-extension/issues/1282)) +- Unnecessary empty line added by formatter ([#1288](https://github.com/sassoftware/vscode-sas-extension/issues/1288)) +- Display context in hover help for data step statement option ([#1306](https://github.com/sassoftware/vscode-sas-extension/issues/1306)) +- SAS log code action should not impact others ([#1302](https://github.com/sassoftware/vscode-sas-extension/issues/1302)) +- The notebook file is opened incorrectly after renaming ([#1289](https://github.com/sassoftware/vscode-sas-extension/issues/1289)) +- Resolve breaking changes by AG Grid 33 ([#1334](https://github.com/sassoftware/vscode-sas-extension/issues/1334)) + ## [v1.12.0] - 2024-11-25 ### Added diff --git a/client/package-lock.json b/client/package-lock.json index ba8c938a2..b5de6a183 100644 --- a/client/package-lock.json +++ b/client/package-lock.json @@ -9,16 +9,16 @@ "version": "0.0.1", "license": "Apache-2.0", "dependencies": { - "ag-grid-community": "^32.3.2", - "ag-grid-react": "^32.3.2", - "axios": "^1.7.7", + "ag-grid-community": "^33.0.2", + "ag-grid-react": "^33.0.2", + "axios": "^1.7.9", "media-typer": "^1.1.0", "react": "^18.3.1", "react-dom": "^18.3.1", "ssh2": "^1.15.0", - "uuid": "^11.0.2", - "vscode-languageclient": "^9.0.1", - "zustand": "^5.0.0" + "uuid": "^11.0.3", + "vscode-languageclient": "^10.0.0-next.2", + "zustand": "^5.0.2" }, "devDependencies": { "@types/react": "^18.3.3", @@ -30,7 +30,7 @@ "@vscode/test-electron": "^2.4.1" }, "engines": { - "vscode": "^1.82.0" + "vscode": "^1.89.0" } }, "node_modules/@types/node": { @@ -108,24 +108,24 @@ } }, "node_modules/ag-charts-types": { - "version": "10.3.1", - "resolved": "https://registry.npmjs.org/ag-charts-types/-/ag-charts-types-10.3.1.tgz", - "integrity": "sha512-oZvu9vJLk6lmzaYi0TmVVmHFZJpVNFziU0bnllx4wR3muXCmnxz5LouKIZ8CYnNiC7VO5HmHNlFu+0DmEO5zxg==" + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/ag-charts-types/-/ag-charts-types-11.0.0.tgz", + "integrity": "sha512-8KUCZtKaUNjxqvo5E71sP7nBpRHMSv81anK93UCMMWYZC3iBSEF1pxfYS2/GEM9oo5VP65mevDJsq10dp0vooQ==" }, "node_modules/ag-grid-community": { - "version": "32.3.2", - "resolved": "https://registry.npmjs.org/ag-grid-community/-/ag-grid-community-32.3.2.tgz", - "integrity": "sha512-ZrgS7+F0JKV6+sy7VQdt+9xhOJd6TAnXXEmw20ByG6AZdcIDEKBRHW7TfNwUqisXJjGKoaXnYByuMz7rL9PcPg==", + "version": "33.0.2", + "resolved": "https://registry.npmjs.org/ag-grid-community/-/ag-grid-community-33.0.2.tgz", + "integrity": "sha512-ymD2ADPVfsAMNoKRB9BvKYPWo+MSJ2L9gOwiwyBS1TykNwf0bL7kfQParZXcY+h+b8JpfeVbx4oGDLZBEe3Gag==", "dependencies": { - "ag-charts-types": "10.3.1" + "ag-charts-types": "11.0.0" } }, "node_modules/ag-grid-react": { - "version": "32.3.2", - "resolved": "https://registry.npmjs.org/ag-grid-react/-/ag-grid-react-32.3.2.tgz", - "integrity": "sha512-jKYiqf2KJI3foIcJHwRzK6ZddXGX3nkxV16aRnOUm9DzFzHKTrgdfxKS+zvhS0TzlkmqRGmq3M4WEgPcuHZH9A==", + "version": "33.0.2", + "resolved": "https://registry.npmjs.org/ag-grid-react/-/ag-grid-react-33.0.2.tgz", + "integrity": "sha512-qgwnNoBZ18H18kCHnwHoGvH1U9nl2CHbSmlfCtg3TC1NUr17IjoiXMSqA0gVdsAo3LyLPN4fYatwQsVIpiWmvw==", "dependencies": { - "ag-grid-community": "32.3.2", + "ag-grid-community": "33.0.2", "prop-types": "^15.8.1" }, "peerDependencies": { @@ -171,9 +171,9 @@ "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" }, "node_modules/axios": { - "version": "1.7.7", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.7.tgz", - "integrity": "sha512-S4kL7XrjgBmvdGut0sN3yJxqYzrDOnivkBiN0OFs6hLiUam3UPvswUo0kqGyhqUZGEOytHyumEdXsAkgCOUf3Q==", + "version": "1.7.9", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.9.tgz", + "integrity": "sha512-LhLcE7Hbiryz8oMDdDptSrWowmB4Bl6RCt6sIJKpRB4XtVf0iEgewX3au/pJqm+Py1kCASkb/FFKjxQaLtxJvw==", "dependencies": { "follow-redirects": "^1.15.6", "form-data": "^4.0.0", @@ -183,7 +183,8 @@ "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "license": "MIT" }, "node_modules/base64-js": { "version": "1.5.1", @@ -242,6 +243,7 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "license": "MIT", "dependencies": { "balanced-match": "^1.0.0" } @@ -602,14 +604,18 @@ } }, "node_modules/minimatch": { - "version": "5.1.6", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", - "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "license": "ISC", "dependencies": { "brace-expansion": "^2.0.1" }, "engines": { - "node": ">=10" + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, "node_modules/ms": { @@ -893,9 +899,9 @@ "dev": true }, "node_modules/uuid": { - "version": "11.0.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-11.0.2.tgz", - "integrity": "sha512-14FfcOJmqdjbBPdDjFQyk/SdT4NySW4eM0zcG+HqbHP5jzuH56xO3J1DGhgs/cEMCfwYi3HQI1gnTO62iaG+tQ==", + "version": "11.0.3", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-11.0.3.tgz", + "integrity": "sha512-d0z310fCWv5dJwnX1Y/MncBAqGMKEzlBb1AOf7z9K8ALnd0utBX/msg/fA0+sbyN1ihbMsLhrBlnl1ak7Wa0rg==", "funding": [ "https://github.com/sponsors/broofa", "https://github.com/sponsors/ctavan" @@ -905,44 +911,48 @@ } }, "node_modules/vscode-jsonrpc": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-8.2.0.tgz", - "integrity": "sha512-C+r0eKJUIfiDIfwJhria30+TYWPtuHJXHtI7J0YlOmKAo7ogxP20T0zxB7HZQIFhIyvoBPwWskjxrvAtfjyZfA==", + "version": "9.0.0-next.4", + "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-9.0.0-next.4.tgz", + "integrity": "sha512-zSVIr58lJSMYKIsZ5P7GtBbv1eEx25eNyOf0NmEzxmn1GhUNJAVAb5hkA1poKUwj1FRMwN6CeyWxZypmr8SsQQ==", + "license": "MIT", "engines": { "node": ">=14.0.0" } }, "node_modules/vscode-languageclient": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/vscode-languageclient/-/vscode-languageclient-9.0.1.tgz", - "integrity": "sha512-JZiimVdvimEuHh5olxhxkht09m3JzUGwggb5eRUkzzJhZ2KjCN0nh55VfiED9oez9DyF8/fz1g1iBV3h+0Z2EA==", + "version": "10.0.0-next.8", + "resolved": "https://registry.npmjs.org/vscode-languageclient/-/vscode-languageclient-10.0.0-next.8.tgz", + "integrity": "sha512-D9inIHgqKayO9Tv0MeLb3XIL76yTuWmKdHqcGZKzjtQrMGJgASJDYWTapu+yAjEpDp0gmVOaCYyIlLB86ncDoQ==", + "license": "MIT", "dependencies": { - "minimatch": "^5.1.0", - "semver": "^7.3.7", - "vscode-languageserver-protocol": "3.17.5" + "minimatch": "^9.0.3", + "semver": "^7.6.0", + "vscode-languageserver-protocol": "3.17.6-next.6" }, "engines": { - "vscode": "^1.82.0" + "vscode": "^1.89.0" } }, "node_modules/vscode-languageserver-protocol": { - "version": "3.17.5", - "resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.17.5.tgz", - "integrity": "sha512-mb1bvRJN8SVznADSGWM9u/b07H7Ecg0I3OgXDuLdn307rl/J3A9YD6/eYOssqhecL27hK1IPZAsaqh00i/Jljg==", + "version": "3.17.6-next.6", + "resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.17.6-next.6.tgz", + "integrity": "sha512-naxM9kc/phpl0kAFNVPejMUWUtzFXdPYY/BtQTYtfbBbHf8sceHOrKkmf6yynZRu1A4oFtRZNqV3wyFRTWqUHw==", + "license": "MIT", "dependencies": { - "vscode-jsonrpc": "8.2.0", - "vscode-languageserver-types": "3.17.5" + "vscode-jsonrpc": "9.0.0-next.4", + "vscode-languageserver-types": "3.17.6-next.4" } }, "node_modules/vscode-languageserver-types": { - "version": "3.17.5", - "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.17.5.tgz", - "integrity": "sha512-Ld1VelNuX9pdF39h2Hgaeb5hEZM2Z3jUrrMgWQAu82jMtZp7p3vJT3BzToKtZI7NgQssZje5o0zryOrhQvzQAg==" + "version": "3.17.6-next.4", + "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.17.6-next.4.tgz", + "integrity": "sha512-SeJTpH/S14EbxOAVaOUoGVqPToqpRTld5QO5Ghig3AlbFJTFF9Wu7srHMfa85L0SX1RYAuuCSFKJVVCxDIk1/Q==", + "license": "MIT" }, "node_modules/zustand": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/zustand/-/zustand-5.0.0.tgz", - "integrity": "sha512-LE+VcmbartOPM+auOjCCLQOsQ05zUTp8RkgwRzefUk+2jISdMMFnxvyTjA4YNWr5ZGXYbVsEMZosttuxUBkojQ==", + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/zustand/-/zustand-5.0.2.tgz", + "integrity": "sha512-8qNdnJVJlHlrKXi50LDqqUNmUbuBjoKLrYQBnoChIbVph7vni+sY+YpvdjXG9YLd/Bxr6scMcR+rm5H3aSqPaw==", "engines": { "node": ">=12.20.0" }, diff --git a/client/package.json b/client/package.json index 38da0a2d5..37eb1fef1 100644 --- a/client/package.json +++ b/client/package.json @@ -6,19 +6,19 @@ "version": "0.0.1", "publisher": "SAS", "engines": { - "vscode": "^1.82.0" + "vscode": "^1.89.0" }, "dependencies": { - "ag-grid-community": "^32.3.2", - "ag-grid-react": "^32.3.2", - "axios": "^1.7.7", + "ag-grid-community": "^33.0.2", + "ag-grid-react": "^33.0.2", + "axios": "^1.7.9", "media-typer": "^1.1.0", "react": "^18.3.1", "react-dom": "^18.3.1", "ssh2": "^1.15.0", - "uuid": "^11.0.2", - "vscode-languageclient": "^9.0.1", - "zustand": "^5.0.0" + "uuid": "^11.0.3", + "vscode-languageclient": "^10.0.0-next.2", + "zustand": "^5.0.2" }, "devDependencies": { "@types/react": "^18.3.3", diff --git a/client/src/browser/extension.ts b/client/src/browser/extension.ts index 5920b2b81..e90103008 100644 --- a/client/src/browser/extension.ts +++ b/client/src/browser/extension.ts @@ -36,8 +36,8 @@ function createWorkerLanguageClient( return new LanguageClient( "sas-lsp", "SAS Language Server", - clientOptions, worker, + clientOptions, ); } diff --git a/client/src/commands/run.ts b/client/src/commands/run.ts index 55d0a31f7..8ccb0ad15 100644 --- a/client/src/commands/run.ts +++ b/client/src/commands/run.ts @@ -47,8 +47,7 @@ async function getSelectedRegions( "sas/getFoldingBlock", { textDocument: { uri: window.activeTextEditor.document.uri.toString() }, - line, - col, + position: { line, col }, }, ); if (block) { diff --git a/client/src/components/ContentNavigator/ContentDataProvider.ts b/client/src/components/ContentNavigator/ContentDataProvider.ts index f6c1afe97..ddf8677cf 100644 --- a/client/src/components/ContentNavigator/ContentDataProvider.ts +++ b/client/src/components/ContentNavigator/ContentDataProvider.ts @@ -18,7 +18,6 @@ import { TabInputText, TextDocument, TextDocumentContentProvider, - ThemeIcon, TreeDataProvider, TreeDragAndDropController, TreeItem, @@ -230,6 +229,7 @@ class ContentDataProvider iconPath: this.iconPathForItem(item), id: item.uid, label: item.name, + resourceUri: uri, }; } @@ -301,18 +301,14 @@ class ContentDataProvider const oldUriToNewUriMap = [[item.vscUri, newUri]]; const newItemIsContainer = getIsContainer(newItem); if (closing !== true && !newItemIsContainer) { - await commands.executeCommand("vscode.openWith", newUri, "default", { - preview: false, - }); + await commands.executeCommand("vscode.open", newUri); } if (closing !== true && newItemIsContainer) { const urisToOpen = getPreviouslyOpenedChildItems( await this.getChildren(newItem), ); for (const [, newUri] of urisToOpen) { - await commands.executeCommand("vscode.openWith", newUri, "default", { - preview: false, - }); + await commands.executeCommand("vscode.open", newUri); } oldUriToNewUriMap.push(...urisToOpen); } @@ -698,7 +694,7 @@ class ContentDataProvider private iconPathForItem( item: ContentItem, - ): ThemeIcon | { light: Uri; dark: Uri } { + ): undefined | { light: Uri; dark: Uri } { const isContainer = getIsContainer(item); let icon = ""; if (isContainer) { @@ -723,11 +719,6 @@ class ContentDataProvider icon = "folder"; break; } - } else { - const extension = item.name.split(".").pop().toLowerCase(); - if (extension === "sas") { - icon = "sasProgramFile"; - } } return icon !== "" @@ -738,7 +729,7 @@ class ContentDataProvider `icons/light/${icon}Light.svg`, ), } - : ThemeIcon.File; + : undefined; } } diff --git a/client/src/components/logViewer/DiagnosticCodeActionProvider.ts b/client/src/components/logViewer/DiagnosticCodeActionProvider.ts index dc34403d0..d6dfab70b 100644 --- a/client/src/components/logViewer/DiagnosticCodeActionProvider.ts +++ b/client/src/components/logViewer/DiagnosticCodeActionProvider.ts @@ -6,6 +6,7 @@ import { CodeActionKind, CodeActionProvider, Command, + Diagnostic, DiagnosticSeverity, ProviderResult, Range, @@ -14,7 +15,7 @@ import { l10n, } from "vscode"; -import { sasDiagnostic } from "./sasDiagnostics"; +import { diagnosticSource, sasDiagnostic } from "./sasDiagnostics"; export class DiagnosticCodeActionProvider implements CodeActionProvider { public static readonly providedCodeActionKinds = [CodeActionKind.QuickFix]; @@ -23,29 +24,32 @@ export class DiagnosticCodeActionProvider implements CodeActionProvider { _range: Range | Selection, context: CodeActionContext, ): ProviderResult<(CodeAction | Command)[]> { - if (context.diagnostics.length === 0) { + const diagnostics = context.diagnostics.filter( + (diagnostic) => diagnostic.source === diagnosticSource, + ); + if (diagnostics.length === 0) { return []; } return [ this.createCodeAction( document, - context, + diagnostics, sasDiagnostic.DiagnosticCommands.IgnoreCommand, ), this.createCodeAction( document, - context, + diagnostics, sasDiagnostic.DiagnosticCommands.IgnoreAllWarningCommand, ), this.createCodeAction( document, - context, + diagnostics, sasDiagnostic.DiagnosticCommands.IgnoreAllErrorCommand, ), this.createCodeAction( document, - context, + diagnostics, sasDiagnostic.DiagnosticCommands.IgnoreAllCommand, ), ]; @@ -53,7 +57,7 @@ export class DiagnosticCodeActionProvider implements CodeActionProvider { private createCodeAction( document: TextDocument, - context: CodeActionContext, + diagnostics: Diagnostic[], command: string, ): CodeAction { const action = new CodeAction("", CodeActionKind.QuickFix); @@ -64,7 +68,7 @@ export class DiagnosticCodeActionProvider implements CodeActionProvider { action.command = { command: command, title: l10n.t("Ignore: current position"), - arguments: [context.diagnostics, document.uri], + arguments: [diagnostics, document.uri], }; break; case sasDiagnostic.DiagnosticCommands.IgnoreAllWarningCommand: diff --git a/client/src/components/logViewer/sasDiagnostics.ts b/client/src/components/logViewer/sasDiagnostics.ts index 19da3f5b4..b2ea7c573 100644 --- a/client/src/components/logViewer/sasDiagnostics.ts +++ b/client/src/components/logViewer/sasDiagnostics.ts @@ -22,6 +22,8 @@ import { DiagnosticCodeActionProvider } from "./DiagnosticCodeActionProvider"; import { Problem } from "./ProblemProcessor"; import { parseLog } from "./logParser"; +export const diagnosticSource = "sas log"; + let diagnosticCollection: DiagnosticCollection; enum DiagnosticCommands { @@ -120,7 +122,7 @@ function constructDiagnostics(problems: Problem[]): Diagnostic[] { message, type === "error" ? DiagnosticSeverity.Error : DiagnosticSeverity.Warning, ); - diagnostic.source = "sas log"; + diagnostic.source = diagnosticSource; return diagnostic; }); diff --git a/client/src/webview/DataViewer.tsx b/client/src/webview/DataViewer.tsx index fe3bffbed..53fd86725 100644 --- a/client/src/webview/DataViewer.tsx +++ b/client/src/webview/DataViewer.tsx @@ -52,6 +52,7 @@ const DataViewer = () => { maxBlocksInCache={10} onGridReady={onGridReady} rowModelType="infinite" + theme="legacy" /> ); diff --git a/client/src/webview/useDataViewer.ts b/client/src/webview/useDataViewer.ts index 2e232a559..7169f12a1 100644 --- a/client/src/webview/useDataViewer.ts +++ b/client/src/webview/useDataViewer.ts @@ -2,7 +2,13 @@ // SPDX-License-Identifier: Apache-2.0 import { useCallback, useEffect, useState } from "react"; -import { ColDef, GridReadyEvent, IGetRowsParams } from "ag-grid-community"; +import { + AllCommunityModule, + ColDef, + GridReadyEvent, + IGetRowsParams, + ModuleRegistry, +} from "ag-grid-community"; import { v4 } from "uuid"; import { TableData } from "../components/LibraryNavigator/types"; @@ -12,6 +18,8 @@ import columnHeaderTemplate from "./columnHeaderTemplate"; declare const acquireVsCodeApi; const vscode = acquireVsCodeApi(); +ModuleRegistry.registerModules([AllCommunityModule]); + const contextMenuHandler = (e) => { e.stopImmediatePropagation(); }; diff --git a/client/test/components/ContentNavigator/ContentDataProvider.test.ts b/client/test/components/ContentNavigator/ContentDataProvider.test.ts index 4caa8fc65..879eece50 100644 --- a/client/test/components/ContentNavigator/ContentDataProvider.test.ts +++ b/client/test/components/ContentNavigator/ContentDataProvider.test.ts @@ -3,7 +3,6 @@ import { DataTransferItem, FileStat, FileType, - ThemeIcon, TreeItem, Uri, authentication, @@ -175,7 +174,6 @@ describe("ContentDataProvider", async function () { const treeItem = await dataProvider.getTreeItem(contentItem); const uri = contentItem.vscUri; const expectedTreeItem: TreeItem = { - iconPath: ThemeIcon.File, id: "unique-id", label: "testFile", command: { @@ -183,6 +181,7 @@ describe("ContentDataProvider", async function () { arguments: [uri], title: "Open SAS File", }, + resourceUri: uri, }; expect(treeItem).to.deep.include(expectedTreeItem); diff --git a/client/testFixture/formatter/expected.sas b/client/testFixture/formatter/expected.sas index a6bc781d1..f54188e54 100644 --- a/client/testFixture/formatter/expected.sas +++ b/client/testFixture/formatter/expected.sas @@ -88,13 +88,11 @@ quit; %macro reportit(request); %if %upcase(&request)=STAT %then %do; - proc means; title "Summary of All Numeric Variables"; run; %end; %else %if %upcase(&request)=PRINTIT %then %do; - proc print; title "Listing of Data"; run; @@ -105,7 +103,6 @@ quit; *region; %macro; - *region; data _null_; *region; diff --git a/package-lock.json b/package-lock.json index 705765953..e13f1e6ed 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,33 +1,34 @@ { "name": "sas-lsp", - "version": "1.12.0", + "version": "1.13.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "sas-lsp", - "version": "1.12.0", + "version": "1.13.0", "hasInstallScript": true, "license": "Apache-2.0", "devDependencies": { "@eslint/compat": "1.2.2", "@trivago/prettier-plugin-sort-imports": "^4.3.0", "@types/chai": "^4.3.11", - "@types/mocha": "^10.0.7", - "@types/node": "^22.9.0", + "@types/mocha": "^10.0.9", + "@types/node": "^22.10.2", "@types/sinon": "^17.0.3", - "@typescript-eslint/eslint-plugin": "^8.13.0", - "@typescript-eslint/parser": "^8.13.0", + "@typescript-eslint/eslint-plugin": "^8.18.0", + "@typescript-eslint/parser": "^8.18.0", "@vscode/l10n-dev": "^0.0.35", + "buffer": "6.0.3", "chai": "^4.4.1", - "concurrently": "^9.0.1", + "concurrently": "^9.1.0", "cross-env": "^7.0.3", "esbuild": "^0.24.0", - "eslint": "^9.14.0", + "eslint": "^9.17.0", "eslint-plugin-react": "^7.37.2", - "eslint-plugin-react-hooks": "^5.0.0", + "eslint-plugin-react-hooks": "^5.1.0", "glob": "^7.2.0", - "mocha": "^10.7.3", + "mocha": "^11.0.1", "nock": "^13.5.5", "papaparse": "^5.4.1", "path-browserify": "^1.0.1", @@ -41,7 +42,7 @@ "webpack-cli": "^5.1.4" }, "engines": { - "vscode": "^1.82.0" + "vscode": "^1.89.0" } }, "node_modules/@aashutoshrathi/word-wrap": { @@ -1035,12 +1036,12 @@ } }, "node_modules/@eslint/config-array": { - "version": "0.18.0", - "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.18.0.tgz", - "integrity": "sha512-fTxvnS1sRMu3+JjXwJG0j/i4RT9u4qJ+lqS/yCGap4lH4zZGzQ7tu+xZqQmcMZq5OBZDL4QRxQzRjkWcGt8IVw==", + "version": "0.19.1", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.19.1.tgz", + "integrity": "sha512-fo6Mtm5mWyKjA/Chy1BYTdn5mGJoDNjC7C64ug20ADsRDGrA85bN3uK3MaKbeRkRuuIEAR5N33Jr1pbm411/PA==", "dev": true, "dependencies": { - "@eslint/object-schema": "^2.1.4", + "@eslint/object-schema": "^2.1.5", "debug": "^4.3.1", "minimatch": "^3.1.2" }, @@ -1049,18 +1050,21 @@ } }, "node_modules/@eslint/core": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.7.0.tgz", - "integrity": "sha512-xp5Jirz5DyPYlPiKat8jaq0EmYvDXKKpzTbxXMpT9eqlRJkRKIz9AGMdlvYjih+im+QlhWrpvVjl8IPC/lHlUw==", + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.9.1.tgz", + "integrity": "sha512-GuUdqkyyzQI5RMIWkHhvTWLCyLo1jNK3vzkSyaExH5kHPDHcuL2VOpHjmMY+y3+NC69qAKToBqldTBgYeLSr9Q==", "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.15" + }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, "node_modules/@eslint/eslintrc": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.1.0.tgz", - "integrity": "sha512-4Bfj15dVJdoy3RfZmmo86RK1Fwzn6SstsvK9JS+BaVKqC6QQQQyXekNaC+g+LKNgkQ+2VhGAzm6hO40AhMR3zQ==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.2.0.tgz", + "integrity": "sha512-grOjVNN8P3hjJn/eIETF1wwd12DdnwFDoyceUJLYYdkpbwq3nLi+4fqrTAONx7XDALqlL220wC/RHSC/QTI/0w==", "dev": true, "dependencies": { "ajv": "^6.12.4", @@ -1081,27 +1085,27 @@ } }, "node_modules/@eslint/js": { - "version": "9.14.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.14.0.tgz", - "integrity": "sha512-pFoEtFWCPyDOl+C6Ift+wC7Ro89otjigCf5vcuWqWgqNSQbRrpjSvdeE6ofLz4dHmyxD5f7gIdGT4+p36L6Twg==", + "version": "9.17.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.17.0.tgz", + "integrity": "sha512-Sxc4hqcs1kTu0iID3kcZDW3JHq2a77HO9P8CP6YEA/FpH3Ll8UXE2r/86Rz9YJLKme39S9vU5OWNjC6Xl0Cr3w==", "dev": true, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, "node_modules/@eslint/object-schema": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.4.tgz", - "integrity": "sha512-BsWiH1yFGjXXS2yvrf5LyuoSIIbPrGUWob917o+BTKuZ7qJdxX8aJLRxs1fS9n6r7vESrq1OUqb68dANcFXuQQ==", + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.5.tgz", + "integrity": "sha512-o0bhxnL89h5Bae5T318nFoFzGy+YE5i/gGkoPAgkmTVdRKTiv3p8JHevPiPaMwoloKfEiiaHlawCqaZMqRm+XQ==", "dev": true, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, "node_modules/@eslint/plugin-kit": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.2.2.tgz", - "integrity": "sha512-CXtq5nR4Su+2I47WPOlWud98Y5Lv8Kyxp2ukhgFx/eW6Blm18VXJO5WuQylPugRo8nbluoi6GvvxBLqHcvqUUw==", + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.2.4.tgz", + "integrity": "sha512-zSkKow6H5Kdm0ZUQUB2kV5JIXqoG0+uH5YADhaEHswm664N9Db8dXSi0nMJpacpMf+MyyglF1vnZohpEg5yUtg==", "dev": true, "dependencies": { "levn": "^0.4.1" @@ -1158,9 +1162,9 @@ } }, "node_modules/@humanwhocodes/retry": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.0.tgz", - "integrity": "sha512-xnRgu9DxZbkWak/te3fcytNyp8MTbuiZIaueg2rgEvBuN55n04nwLYLU9TX/VVlusc9L2ZNXi99nUFNkHXtr5g==", + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.1.tgz", + "integrity": "sha512-c7hNEllBlenFTHBky65mhq8WD2kbN9Q6gk0bTk8lSBvc554jpXSkST1iePudpt7+A/AQvuHs9EMqjHDXMY1lrA==", "dev": true, "engines": { "node": ">=18.18" @@ -1505,18 +1509,18 @@ "dev": true }, "node_modules/@types/mocha": { - "version": "10.0.7", - "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-10.0.7.tgz", - "integrity": "sha512-GN8yJ1mNTcFcah/wKEFIJckJx9iJLoMSzWcfRRuxz/Jk+U6KQNnml+etbtxFK8lPjzOw3zp4Ha/kjSst9fsHYw==", + "version": "10.0.9", + "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-10.0.9.tgz", + "integrity": "sha512-sicdRoWtYevwxjOHNMPTl3vSfJM6oyW8o1wXeI7uww6b6xHg8eBznQDNSGBCDJmsE8UMxP05JgZRtsKbTqt//Q==", "dev": true }, "node_modules/@types/node": { - "version": "22.9.0", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.9.0.tgz", - "integrity": "sha512-vuyHg81vvWA1Z1ELfvLko2c8f34gyA0zaic0+Rllc5lbCnbSyuvb2Oxpm6TAUAC/2xZN3QGqxBNggD1nNR2AfQ==", + "version": "22.10.2", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.2.tgz", + "integrity": "sha512-Xxr6BBRCAOQixvonOye19wnzyDiUtTeqldOOmj3CkeblonbccA12PFwlufvRdrpjXxqnmUaeiU5EOA+7s5diUQ==", "dev": true, "dependencies": { - "undici-types": "~6.19.8" + "undici-types": "~6.20.0" } }, "node_modules/@types/sinon": { @@ -1545,16 +1549,16 @@ "dev": true }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.13.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.13.0.tgz", - "integrity": "sha512-nQtBLiZYMUPkclSeC3id+x4uVd1SGtHuElTxL++SfP47jR0zfkZBJHc+gL4qPsgTuypz0k8Y2GheaDYn6Gy3rg==", + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.18.0.tgz", + "integrity": "sha512-NR2yS7qUqCL7AIxdJUQf2MKKNDVNaig/dEB0GBLU7D+ZdHgK1NoH/3wsgO3OnPVipn51tG3MAwaODEGil70WEw==", "dev": true, "dependencies": { "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "8.13.0", - "@typescript-eslint/type-utils": "8.13.0", - "@typescript-eslint/utils": "8.13.0", - "@typescript-eslint/visitor-keys": "8.13.0", + "@typescript-eslint/scope-manager": "8.18.0", + "@typescript-eslint/type-utils": "8.18.0", + "@typescript-eslint/utils": "8.18.0", + "@typescript-eslint/visitor-keys": "8.18.0", "graphemer": "^1.4.0", "ignore": "^5.3.1", "natural-compare": "^1.4.0", @@ -1569,24 +1573,20 @@ }, "peerDependencies": { "@typescript-eslint/parser": "^8.0.0 || ^8.0.0-alpha.0", - "eslint": "^8.57.0 || ^9.0.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.8.0" } }, "node_modules/@typescript-eslint/parser": { - "version": "8.13.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.13.0.tgz", - "integrity": "sha512-w0xp+xGg8u/nONcGw1UXAr6cjCPU1w0XVyBs6Zqaj5eLmxkKQAByTdV/uGgNN5tVvN/kKpoQlP2cL7R+ajZZIQ==", + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.18.0.tgz", + "integrity": "sha512-hgUZ3kTEpVzKaK3uNibExUYm6SKKOmTU2BOxBSvOYwtJEPdVQ70kZJpPjstlnhCHcuc2WGfSbpKlb/69ttyN5Q==", "dev": true, "dependencies": { - "@typescript-eslint/scope-manager": "8.13.0", - "@typescript-eslint/types": "8.13.0", - "@typescript-eslint/typescript-estree": "8.13.0", - "@typescript-eslint/visitor-keys": "8.13.0", + "@typescript-eslint/scope-manager": "8.18.0", + "@typescript-eslint/types": "8.18.0", + "@typescript-eslint/typescript-estree": "8.18.0", + "@typescript-eslint/visitor-keys": "8.18.0", "debug": "^4.3.4" }, "engines": { @@ -1597,22 +1597,18 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.8.0" } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "8.13.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.13.0.tgz", - "integrity": "sha512-XsGWww0odcUT0gJoBZ1DeulY1+jkaHUciUq4jKNv4cpInbvvrtDoyBH9rE/n2V29wQJPk8iCH1wipra9BhmiMA==", + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.18.0.tgz", + "integrity": "sha512-PNGcHop0jkK2WVYGotk/hxj+UFLhXtGPiGtiaWgVBVP1jhMoMCHlTyJA+hEj4rszoSdLTK3fN4oOatrL0Cp+Xw==", "dev": true, "dependencies": { - "@typescript-eslint/types": "8.13.0", - "@typescript-eslint/visitor-keys": "8.13.0" + "@typescript-eslint/types": "8.18.0", + "@typescript-eslint/visitor-keys": "8.18.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -1623,13 +1619,13 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "8.13.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.13.0.tgz", - "integrity": "sha512-Rqnn6xXTR316fP4D2pohZenJnp+NwQ1mo7/JM+J1LWZENSLkJI8ID8QNtlvFeb0HnFSK94D6q0cnMX6SbE5/vA==", + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.18.0.tgz", + "integrity": "sha512-er224jRepVAVLnMF2Q7MZJCq5CsdH2oqjP4dT7K6ij09Kyd+R21r7UVJrF0buMVdZS5QRhDzpvzAxHxabQadow==", "dev": true, "dependencies": { - "@typescript-eslint/typescript-estree": "8.13.0", - "@typescript-eslint/utils": "8.13.0", + "@typescript-eslint/typescript-estree": "8.18.0", + "@typescript-eslint/utils": "8.18.0", "debug": "^4.3.4", "ts-api-utils": "^1.3.0" }, @@ -1640,16 +1636,15 @@ "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.8.0" } }, "node_modules/@typescript-eslint/types": { - "version": "8.13.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.13.0.tgz", - "integrity": "sha512-4cyFErJetFLckcThRUFdReWJjVsPCqyBlJTi6IDEpc1GWCIIZRFxVppjWLIMcQhNGhdWJJRYFHpHoDWvMlDzng==", + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.18.0.tgz", + "integrity": "sha512-FNYxgyTCAnFwTrzpBGq+zrnoTO4x0c1CKYY5MuUTzpScqmY5fmsh2o3+57lqdI3NZucBDCzDgdEbIaNfAjAHQA==", "dev": true, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -1660,13 +1655,13 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.13.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.13.0.tgz", - "integrity": "sha512-v7SCIGmVsRK2Cy/LTLGN22uea6SaUIlpBcO/gnMGT/7zPtxp90bphcGf4fyrCQl3ZtiBKqVTG32hb668oIYy1g==", + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.18.0.tgz", + "integrity": "sha512-rqQgFRu6yPkauz+ms3nQpohwejS8bvgbPyIDq13cgEDbkXt4LH4OkDMT0/fN1RUtzG8e8AKJyDBoocuQh8qNeg==", "dev": true, "dependencies": { - "@typescript-eslint/types": "8.13.0", - "@typescript-eslint/visitor-keys": "8.13.0", + "@typescript-eslint/types": "8.18.0", + "@typescript-eslint/visitor-keys": "8.18.0", "debug": "^4.3.4", "fast-glob": "^3.3.2", "is-glob": "^4.0.3", @@ -1681,10 +1676,8 @@ "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } + "peerDependencies": { + "typescript": ">=4.8.4 <5.8.0" } }, "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { @@ -1712,15 +1705,15 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "8.13.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.13.0.tgz", - "integrity": "sha512-A1EeYOND6Uv250nybnLZapeXpYMl8tkzYUxqmoKAWnI4sei3ihf2XdZVd+vVOmHGcp3t+P7yRrNsyyiXTvShFQ==", + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.18.0.tgz", + "integrity": "sha512-p6GLdY383i7h5b0Qrfbix3Vc3+J2k6QWw6UMUeY5JGfm3C5LbZ4QIZzJNoNOfgyRe0uuYKjvVOsO/jD4SJO+xg==", "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", - "@typescript-eslint/scope-manager": "8.13.0", - "@typescript-eslint/types": "8.13.0", - "@typescript-eslint/typescript-estree": "8.13.0" + "@typescript-eslint/scope-manager": "8.18.0", + "@typescript-eslint/types": "8.18.0", + "@typescript-eslint/typescript-estree": "8.18.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -1730,17 +1723,18 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0" + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.8.0" } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.13.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.13.0.tgz", - "integrity": "sha512-7N/+lztJqH4Mrf0lb10R/CbI1EaAMMGyF5y0oJvFoAhafwgiRA7TXyd8TFn8FC8k5y2dTsYogg238qavRGNnlw==", + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.18.0.tgz", + "integrity": "sha512-pCh/qEA8Lb1wVIqNvBke8UaRjJ6wrAWkJO5yyIbs8Yx6TNGYyfNjOo61tLv+WwLvoLPp4BQ8B7AHKijl8NGUfw==", "dev": true, "dependencies": { - "@typescript-eslint/types": "8.13.0", - "eslint-visitor-keys": "^3.4.3" + "@typescript-eslint/types": "8.18.0", + "eslint-visitor-keys": "^4.2.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -1750,6 +1744,18 @@ "url": "https://opencollective.com/typescript-eslint" } }, + "node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz", + "integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==", + "dev": true, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, "node_modules/@vscode/l10n-dev": { "version": "0.0.35", "resolved": "https://registry.npmjs.org/@vscode/l10n-dev/-/l10n-dev-0.0.35.tgz", @@ -2320,6 +2326,26 @@ "dev": true, "license": "MIT" }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, "node_modules/binary-extensions": { "version": "2.2.0", "dev": true, @@ -2392,6 +2418,30 @@ "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" } }, + "node_modules/buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, "node_modules/buffer-from": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", @@ -2592,10 +2642,11 @@ "license": "MIT" }, "node_modules/concurrently": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/concurrently/-/concurrently-9.0.1.tgz", - "integrity": "sha512-wYKvCd/f54sTXJMSfV6Ln/B8UrfLBKOYa+lzc6CHay3Qek+LorVSBdMVfyewFhRbH0Rbabsk4D+3PL/VjQ5gzg==", + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/concurrently/-/concurrently-9.1.0.tgz", + "integrity": "sha512-VxkzwMAn4LP7WyMnJNbHN5mKV9L2IbyDjpzemKr99sXNR3GqRNMMHdm7prV1ws9wg7ETj6WUkNOigZVsptwbgg==", "dev": true, + "license": "MIT", "dependencies": { "chalk": "^4.1.2", "lodash": "^4.17.21", @@ -2697,9 +2748,10 @@ } }, "node_modules/cross-spawn": { - "version": "7.0.3", + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", "dev": true, - "license": "MIT", "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", @@ -3218,26 +3270,26 @@ } }, "node_modules/eslint": { - "version": "9.14.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.14.0.tgz", - "integrity": "sha512-c2FHsVBr87lnUtjP4Yhvk4yEhKrQavGafRA/Se1ouse8PfbfC/Qh9Mxa00yWsZRlqeUB9raXip0aiiUZkgnr9g==", + "version": "9.17.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.17.0.tgz", + "integrity": "sha512-evtlNcpJg+cZLcnVKwsai8fExnqjGPicK7gnUtlNuzu+Fv9bI0aLpND5T44VLQtoMEnI57LoXO9XAkIXwohKrA==", "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.12.1", - "@eslint/config-array": "^0.18.0", - "@eslint/core": "^0.7.0", - "@eslint/eslintrc": "^3.1.0", - "@eslint/js": "9.14.0", - "@eslint/plugin-kit": "^0.2.0", + "@eslint/config-array": "^0.19.0", + "@eslint/core": "^0.9.0", + "@eslint/eslintrc": "^3.2.0", + "@eslint/js": "9.17.0", + "@eslint/plugin-kit": "^0.2.3", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", - "@humanwhocodes/retry": "^0.4.0", + "@humanwhocodes/retry": "^0.4.1", "@types/estree": "^1.0.6", "@types/json-schema": "^7.0.15", "ajv": "^6.12.4", "chalk": "^4.0.0", - "cross-spawn": "^7.0.2", + "cross-spawn": "^7.0.6", "debug": "^4.3.2", "escape-string-regexp": "^4.0.0", "eslint-scope": "^8.2.0", @@ -3256,8 +3308,7 @@ "lodash.merge": "^4.6.2", "minimatch": "^3.1.2", "natural-compare": "^1.4.0", - "optionator": "^0.9.3", - "text-table": "^0.2.0" + "optionator": "^0.9.3" }, "bin": { "eslint": "bin/eslint.js" @@ -3310,9 +3361,9 @@ } }, "node_modules/eslint-plugin-react-hooks": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-5.0.0.tgz", - "integrity": "sha512-hIOwI+5hYGpJEc4uPRmz2ulCjAGD/N13Lukkh8cLV0i2IRk/bdZDYjgLVHj+U9Z704kLIdIO6iueGvxNur0sgw==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-5.1.0.tgz", + "integrity": "sha512-mpJRtPgHN2tNAvZ35AMfqeB3Xqeo273QxrHJsbBEPWODRM4r0yB6jfoROqKEYrOn27UtRPpcpHc2UqyBSuUNTw==", "dev": true, "engines": { "node": ">=10" @@ -3990,6 +4041,26 @@ "node": ">= 6" } }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, "node_modules/ignore": { "version": "5.3.1", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz", @@ -4847,9 +4918,9 @@ } }, "node_modules/mocha": { - "version": "10.7.3", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.7.3.tgz", - "integrity": "sha512-uQWxAu44wwiACGqjbPYmjo7Lg8sFrS3dQe7PP2FQI+woptP4vZXSMcfMyFL/e1yFEeEpV4RtyTpZROOKmxis+A==", + "version": "11.0.1", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-11.0.1.tgz", + "integrity": "sha512-+3GkODfsDG71KSCQhc4IekSW+ItCK/kiez1Z28ksWvYhKXV/syxMlerR/sC7whDp7IyreZ4YxceMLdTs5hQE8A==", "dev": true, "dependencies": { "ansi-colors": "^4.1.3", @@ -4859,7 +4930,7 @@ "diff": "^5.2.0", "escape-string-regexp": "^4.0.0", "find-up": "^5.0.0", - "glob": "^8.1.0", + "glob": "^10.4.5", "he": "^1.2.0", "js-yaml": "^4.1.0", "log-symbols": "^4.1.0", @@ -4878,38 +4949,68 @@ "mocha": "bin/mocha.js" }, "engines": { - "node": ">= 14.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, "node_modules/mocha/node_modules/brace-expansion": { "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", "dev": true, - "license": "MIT", "dependencies": { "balanced-match": "^1.0.0" } }, "node_modules/mocha/node_modules/glob": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz", - "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==", - "deprecated": "Glob versions prior to v9 are no longer supported", + "version": "10.4.5", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", + "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", "dev": true, - "license": "ISC", "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^5.0.1", - "once": "^1.3.0" + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/mocha/node_modules/glob/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" }, "engines": { - "node": ">=12" + "node": ">=16 || 14 >=14.17" }, "funding": { "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/mocha/node_modules/jackspeak": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", + "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", + "dev": true, + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, "node_modules/mocha/node_modules/minimatch": { "version": "5.1.6", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", @@ -4922,6 +5023,15 @@ "node": ">=10" } }, + "node_modules/mocha/node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "dev": true, + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, "node_modules/mocha/node_modules/ms": { "version": "2.1.3", "dev": true, @@ -5201,6 +5311,12 @@ "node": ">=6" } }, + "node_modules/package-json-from-dist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", + "dev": true + }, "node_modules/papaparse": { "version": "5.4.1", "resolved": "https://registry.npmjs.org/papaparse/-/papaparse-5.4.1.tgz", @@ -5256,29 +5372,26 @@ "license": "MIT" }, "node_modules/path-scurry": { - "version": "1.9.2", - "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.9.2.tgz", - "integrity": "sha512-qSDLy2aGFPm8i4rsbHd4MNyTcrzHFsLQykrtbuGRknZZCBBVXSv2tSCDN2Cg6Rt/GFRw8GoW9y9Ecw5rIPG1sg==", + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", "dev": true, "dependencies": { - "lru-cache": "^9.1.1", - "minipass": "^5.0.0 || ^6.0.2" + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" }, "engines": { - "node": ">=16 || 14 >=14.17" + "node": ">=16 || 14 >=14.18" }, "funding": { "url": "https://github.com/sponsors/isaacs" } }, "node_modules/path-scurry/node_modules/lru-cache": { - "version": "9.1.2", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-9.1.2.tgz", - "integrity": "sha512-ERJq3FOzJTxBbFjZ7iDs+NiK4VI9Wz+RdrrAB8dio1oV+YvdPzUEE4QNiT2VD51DkIbCYRUUzCRkssXCHqSnKQ==", - "dev": true, - "engines": { - "node": "14 || >=16.14" - } + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true }, "node_modules/path-to-regexp": { "version": "1.8.0", @@ -6182,11 +6295,6 @@ } } }, - "node_modules/text-table": { - "version": "0.2.0", - "dev": true, - "license": "MIT" - }, "node_modules/to-fast-properties": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", @@ -6540,9 +6648,9 @@ } }, "node_modules/undici-types": { - "version": "6.19.8", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz", - "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==", + "version": "6.20.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", + "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==", "dev": true }, "node_modules/update-browserslist-db": { diff --git a/package.json b/package.json index b81abbf1b..777376b11 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "sas-lsp", "displayName": "SAS", "description": "Official SAS Language Extension for VS Code", - "version": "1.12.0", + "version": "1.13.0", "categories": [ "Programming Languages", "Data Science", @@ -13,7 +13,7 @@ "license": "Apache-2.0", "icon": "icons/sas.png", "engines": { - "vscode": "^1.82.0" + "vscode": "^1.89.0" }, "activationEvents": [ "onWebviewPanel:SASResultPanel", @@ -1269,21 +1269,22 @@ "@eslint/compat": "1.2.2", "@trivago/prettier-plugin-sort-imports": "^4.3.0", "@types/chai": "^4.3.11", - "@types/mocha": "^10.0.7", - "@types/node": "^22.9.0", + "@types/mocha": "^10.0.9", + "@types/node": "^22.10.2", "@types/sinon": "^17.0.3", - "@typescript-eslint/eslint-plugin": "^8.13.0", - "@typescript-eslint/parser": "^8.13.0", + "@typescript-eslint/eslint-plugin": "^8.18.0", + "@typescript-eslint/parser": "^8.18.0", "@vscode/l10n-dev": "^0.0.35", + "buffer": "6.0.3", "chai": "^4.4.1", - "concurrently": "^9.0.1", + "concurrently": "^9.1.0", "esbuild": "^0.24.0", "cross-env": "^7.0.3", - "eslint": "^9.14.0", + "eslint": "^9.17.0", "eslint-plugin-react": "^7.37.2", - "eslint-plugin-react-hooks": "^5.0.0", + "eslint-plugin-react-hooks": "^5.1.0", "glob": "^7.2.0", - "mocha": "^10.7.3", + "mocha": "^11.0.1", "nock": "^13.5.5", "papaparse": "^5.4.1", "path-browserify": "^1.0.1", diff --git a/server/package-lock.json b/server/package-lock.json index 4a4434473..8510eb22d 100644 --- a/server/package-lock.json +++ b/server/package-lock.json @@ -9,39 +9,451 @@ "version": "0.0.1", "license": "Apache-2.0", "dependencies": { - "vscode-languageserver": "^9.0.1", - "vscode-languageserver-textdocument": "^1.0.11" + "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" }, "engines": { "node": "*" } }, + "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/@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/@yarnpkg/fslib": { + "version": "2.10.4", + "resolved": "https://registry.npmjs.org/@yarnpkg/fslib/-/fslib-2.10.4.tgz", + "integrity": "sha512-WhaLwvXEMjCjGxOraQx+Qtmst13iAPOlSElSZfQFdLohva5owlqACRapJ78zZFEW6M9ArqdQlZaHKVN5/mM+SA==", + "dependencies": { + "@yarnpkg/libzip": "^2.3.0", + "tslib": "^1.13.0" + }, + "engines": { + "node": ">=12 <14 || 14.2 - 14.9 || >14.10.0" + } + }, + "node_modules/@yarnpkg/libzip": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@yarnpkg/libzip/-/libzip-2.3.0.tgz", + "integrity": "sha512-6xm38yGVIa6mKm/DUCF2zFFJhERh/QWp1ufm4cNUvxsONBmfPg8uZ9pZBdOmF6qFGr/HlT6ABBkCSx/dlEtvWg==", + "dependencies": { + "@types/emscripten": "^1.39.6", + "tslib": "^1.13.0" + }, + "engines": { + "node": ">=12 <14 || 14.2 - 14.9 || >14.10.0" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/array-back": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/array-back/-/array-back-3.1.0.tgz", + "integrity": "sha512-TkuxA4UCOvxuDK6NZYXCalszEzj+TLszyASooky+i742l9TqsOdYCMJJupxRic61hwquNtppB3hgcuq9SVSH1Q==", + "engines": { + "node": ">=6" + } + }, + "node_modules/binary-extensions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==" + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "node_modules/command-line-args": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/command-line-args/-/command-line-args-5.2.1.tgz", + "integrity": "sha512-H4UfQhZyakIjC74I9d34fGYDwk3XpSr17QhEd0Q3I9Xq1CETHo4Hcuo87WyWHpAF1aSLjLRf5lD9ZGX2qStUvg==", + "dependencies": { + "array-back": "^3.1.0", + "find-replace": "^3.0.0", + "lodash.camelcase": "^4.3.0", + "typical": "^4.0.0" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "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/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/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-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/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/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/lodash.camelcase": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", + "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==" + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pyright-internal-browser": { + "version": "1.1.367", + "resolved": "https://registry.npmjs.org/pyright-internal-browser/-/pyright-internal-browser-1.1.367.tgz", + "integrity": "sha512-mIbtpRRDZQ9j9NgP3oaKRI2usc2xfQ7xKrCMk/+wLprLintkFuQqkZX+VVDq3nF/QlzFE37+UiLMbKqeYTsIbw==", + "dependencies": { + "@iarna/toml": "2.2.5", + "@yarnpkg/fslib": "2.10.4", + "@yarnpkg/libzip": "2.3.0", + "chalk": "^4.1.2", + "chokidar": "^3.6.0", + "command-line-args": "^5.2.1", + "jsonc-parser": "^3.2.1", + "leven": "3.1.0", + "source-map-support": "^0.5.21", + "tmp": "^0.2.1", + "vscode-jsonrpc": "^9.0.0-next.2", + "vscode-languageserver": "^10.0.0-next.2", + "vscode-languageserver-textdocument": "1.0.11", + "vscode-languageserver-types": "^3.17.6-next.3", + "vscode-uri": "^3.0.8" + } + }, + "node_modules/pyright-internal-node": { + "version": "1.1.367", + "resolved": "https://registry.npmjs.org/pyright-internal-node/-/pyright-internal-node-1.1.367.tgz", + "integrity": "sha512-23v82uTBC8XmRvXrGQQvz0wW4thfaiAIzmV8SBhuMZn6tcllUvlv5BEJ6YFENBSkwWZgguS+p47JZvuxR0TKoQ==", + "dependencies": { + "@iarna/toml": "2.2.5", + "@yarnpkg/fslib": "2.10.4", + "@yarnpkg/libzip": "2.3.0", + "chalk": "^4.1.2", + "chokidar": "^3.6.0", + "command-line-args": "^5.2.1", + "jsonc-parser": "^3.2.1", + "leven": "3.1.0", + "source-map-support": "^0.5.21", + "tmp": "^0.2.1", + "vscode-jsonrpc": "^9.0.0-next.2", + "vscode-languageserver": "^10.0.0-next.2", + "vscode-languageserver-textdocument": "1.0.11", + "vscode-languageserver-types": "^3.17.6-next.3", + "vscode-uri": "^3.0.8" + } + }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/tmp": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.3.tgz", + "integrity": "sha512-nZD7m9iCPC5g0pYmcaxogYKggSfLsdxl8of3Q/oIbqCqLLIO9IAF0GWjX1z9NZRHPiXv8Wex4yDCaZsgEw0Y8w==", + "engines": { + "node": ">=14.14" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + }, + "node_modules/typical": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/typical/-/typical-4.0.0.tgz", + "integrity": "sha512-VAH4IvQ7BDFYglMd7BPRDfLgxZZX4O4TFcRDA6EN5X7erNJJq+McIEp8np9aVtxrCJ6qx4GTYVfOWNjcqwZgRw==", + "engines": { + "node": ">=8" + } + }, "node_modules/vscode-jsonrpc": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-8.2.0.tgz", - "integrity": "sha512-C+r0eKJUIfiDIfwJhria30+TYWPtuHJXHtI7J0YlOmKAo7ogxP20T0zxB7HZQIFhIyvoBPwWskjxrvAtfjyZfA==", + "version": "9.0.0-next.4", + "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-9.0.0-next.4.tgz", + "integrity": "sha512-zSVIr58lJSMYKIsZ5P7GtBbv1eEx25eNyOf0NmEzxmn1GhUNJAVAb5hkA1poKUwj1FRMwN6CeyWxZypmr8SsQQ==", + "license": "MIT", "engines": { "node": ">=14.0.0" } }, "node_modules/vscode-languageserver": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/vscode-languageserver/-/vscode-languageserver-9.0.1.tgz", - "integrity": "sha512-woByF3PDpkHFUreUa7Hos7+pUWdeWMXRd26+ZX2A8cFx6v/JPTtd4/uN0/jB6XQHYaOlHbio03NTHCqrgG5n7g==", + "version": "10.0.0-next.6", + "resolved": "https://registry.npmjs.org/vscode-languageserver/-/vscode-languageserver-10.0.0-next.6.tgz", + "integrity": "sha512-0Lh1nhQfSxo5Ob+ayYO1QTIsDix2/Lc72Urm1KZrCFxK5zIFYaEh3QFeM9oZih4Rzs0ZkQPXXnoHtpvs5GT+Zw==", + "license": "MIT", "dependencies": { - "vscode-languageserver-protocol": "3.17.5" + "vscode-languageserver-protocol": "3.17.6-next.6" }, "bin": { "installServerIntoExtension": "bin/installServerIntoExtension" } }, "node_modules/vscode-languageserver-protocol": { - "version": "3.17.5", - "resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.17.5.tgz", - "integrity": "sha512-mb1bvRJN8SVznADSGWM9u/b07H7Ecg0I3OgXDuLdn307rl/J3A9YD6/eYOssqhecL27hK1IPZAsaqh00i/Jljg==", + "version": "3.17.6-next.6", + "resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.17.6-next.6.tgz", + "integrity": "sha512-naxM9kc/phpl0kAFNVPejMUWUtzFXdPYY/BtQTYtfbBbHf8sceHOrKkmf6yynZRu1A4oFtRZNqV3wyFRTWqUHw==", + "license": "MIT", "dependencies": { - "vscode-jsonrpc": "8.2.0", - "vscode-languageserver-types": "3.17.5" + "vscode-jsonrpc": "9.0.0-next.4", + "vscode-languageserver-types": "3.17.6-next.4" } }, "node_modules/vscode-languageserver-textdocument": { @@ -50,9 +462,15 @@ "integrity": "sha512-X+8T3GoiwTVlJbicx/sIAF+yuJAqz8VvwJyoMVhwEMoEKE/fkDmrqUgDMyBECcM2A2frVZIUj5HI/ErRXCfOeA==" }, "node_modules/vscode-languageserver-types": { - "version": "3.17.5", - "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.17.5.tgz", - "integrity": "sha512-Ld1VelNuX9pdF39h2Hgaeb5hEZM2Z3jUrrMgWQAu82jMtZp7p3vJT3BzToKtZI7NgQssZje5o0zryOrhQvzQAg==" + "version": "3.17.6-next.4", + "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.17.6-next.4.tgz", + "integrity": "sha512-SeJTpH/S14EbxOAVaOUoGVqPToqpRTld5QO5Ghig3AlbFJTFF9Wu7srHMfa85L0SX1RYAuuCSFKJVVCxDIk1/Q==", + "license": "MIT" + }, + "node_modules/vscode-uri": { + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-3.0.8.tgz", + "integrity": "sha512-AyFQ0EVmsOZOlAnxoFOGOq1SQDWAB7C6aqMGS23svWAllfOaxbuFvcT8D1i8z3Gyn8fraVeZNNmN6e9bxxXkKw==" } } } diff --git a/server/package.json b/server/package.json index 32d96e04d..3609e4fef 100644 --- a/server/package.json +++ b/server/package.json @@ -8,7 +8,9 @@ "node": "*" }, "dependencies": { - "vscode-languageserver": "^9.0.1", - "vscode-languageserver-textdocument": "^1.0.11" + "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" } -} +} \ No newline at end of file diff --git a/server/src/browser/server.ts b/server/src/browser/server.ts index 0742cd2e1..82f4a96ac 100644 --- a/server/src/browser/server.ts +++ b/server/src/browser/server.ts @@ -1,19 +1,19 @@ // Copyright © 2022, SAS Institute Inc., Cary, NC, USA. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 +import { Connection } from "vscode-languageserver"; import { BrowserMessageReader, BrowserMessageWriter, createConnection, } from "vscode-languageserver/browser"; -import { init } from "../server"; +import { PyrightLanguageProviderBrowser } from "../python/browser/PyrightLanguageProviderBrowser"; +import { runServer } from "../server"; /* browser specific setup code */ - const messageReader = new BrowserMessageReader(self); const messageWriter = new BrowserMessageWriter(self); -const connection = createConnection(messageReader, messageWriter); +const connection: Connection = createConnection(messageReader, messageWriter); -/* from here on, all code is non-browser specific and could be shared with a regular extension */ -init(connection); +runServer(connection, new PyrightLanguageProviderBrowser(connection, 1)); diff --git a/server/src/node/server.ts b/server/src/node/server.ts index 2099dceb3..c01771b0c 100644 --- a/server/src/node/server.ts +++ b/server/src/node/server.ts @@ -1,12 +1,11 @@ // Copyright © 2022, SAS Institute Inc., Cary, NC, USA. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 +import { Connection } from "vscode-languageserver"; import { ProposedFeatures, createConnection } from "vscode-languageserver/node"; -import { init } from "../server"; +import { PyrightLanguageProviderNode } from "../python/node/PyrightLanguageProviderNode"; +import { runServer } from "../server"; -// Create a connection for the server, using Node's IPC as a transport. -// Also include all preview / proposed LSP features. -const connection = createConnection(ProposedFeatures.all); +const connection: Connection = createConnection(ProposedFeatures.all); -// platform independant initialization -init(connection); +runServer(connection, new PyrightLanguageProviderNode(connection, 1)); diff --git a/server/src/python/PyrightLanguageProvider.ts b/server/src/python/PyrightLanguageProvider.ts new file mode 100644 index 000000000..1ef554183 --- /dev/null +++ b/server/src/python/PyrightLanguageProvider.ts @@ -0,0 +1,8 @@ +// Copyright © 2024, SAS Institute Inc., Cary, NC, USA. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +import type { PyrightLanguageProviderBrowser } from "./browser/PyrightLanguageProviderBrowser"; +import type { PyrightLanguageProviderNode } from "./node/PyrightLanguageProviderNode"; + +export type PyrightLanguageProvider = + | PyrightLanguageProviderNode + | PyrightLanguageProviderBrowser; diff --git a/server/src/python/browser/PyrightLanguageProviderBrowser.ts b/server/src/python/browser/PyrightLanguageProviderBrowser.ts new file mode 100644 index 000000000..836b84e8c --- /dev/null +++ b/server/src/python/browser/PyrightLanguageProviderBrowser.ts @@ -0,0 +1,278 @@ +// Copyright © 2022-2024, SAS Institute Inc., Cary, NC, USA. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +import { + CallHierarchyIncomingCallsParams, + CallHierarchyItem, + CallHierarchyOutgoingCall, + CallHierarchyOutgoingCallsParams, + CallHierarchyPrepareParams, + CancellationToken, + CodeAction, + CodeActionParams, + Command, + CompletionItem, + CompletionList, + CompletionParams, + Connection, + Declaration, + DeclarationLink, + Definition, + DefinitionLink, + DidChangeConfigurationParams, + DidChangeWatchedFilesParams, + DidCloseTextDocumentParams, + DidOpenTextDocumentParams, + DocumentHighlight, + DocumentHighlightParams, + DocumentSymbol, + DocumentSymbolParams, + ExecuteCommandParams, + HoverParams, + InitializeParams, + InitializeResult, + LSPAny, + Location, + PrepareRenameParams, + ReferenceParams, + RenameParams, + ResultProgressReporter, + SignatureHelpParams, + SymbolInformation, + TextDocumentPositionParams, + WorkDoneProgressReporter, + WorkspaceSymbol, + WorkspaceSymbolParams, +} from "vscode-languageserver"; +import { TextDocument } from "vscode-languageserver-textdocument"; + +import { IPythonMode } from "pyright-internal-browser/dist/packages/pyright-internal/src/analyzer/sourceFile"; +import { FileSystem } from "pyright-internal-browser/dist/packages/pyright-internal/src/common/fileSystem"; +import { PyrightServer } from "pyright-internal-browser/dist/packages/pyright-internal/src/server"; + +import { LanguageServiceProvider } from "../../sas/LanguageServiceProvider"; +import { extractPythonCodes } from "../utils"; + +export class PyrightLanguageProviderBrowser extends PyrightServer { + protected sasLspProvider?: (uri: string) => LanguageServiceProvider; + + constructor( + connection: Connection, + maxWorkers: number, + realFileSystem?: FileSystem, + ) { + // eslint-disable-next-line @typescript-eslint/consistent-type-assertions + super({ ...connection, listen() {} } as LSPAny, maxWorkers, realFileSystem); + } + + public setSasLspProvider( + provider: (uri: string) => LanguageServiceProvider, + ): void { + this.sasLspProvider = provider; + } + + public getClientCapabilities() { + return this.client; + } + + protected setupConnection( + // eslint-disable-next-line @typescript-eslint/no-unused-vars + supportedCommands: string[], + // eslint-disable-next-line @typescript-eslint/no-unused-vars + supportedCodeActions: string[], + ): void { + return; + } + + public onInitialized(): void { + this.updateSettingsForAllWorkspaces(); + super.onInitialized(); + } + + public async initialize( + params: InitializeParams, + supportedCommands: string[], + supportedCodeActions: string[], + ): Promise { + return super.initialize(params, supportedCommands, supportedCodeActions); + } + + public addContentChange(doc: TextDocument): void { + const languageService = this.sasLspProvider!(doc.uri); + const pythonDoc = extractPythonCodes(doc, languageService); + + this.onDidChangeTextDocument({ + textDocument: doc, + contentChanges: [{ text: pythonDoc }], + }); + } + + public async onHover(params: HoverParams, token: CancellationToken) { + return await super.onHover(params, token); + } + + public async onDidOpenTextDocument( + params: DidOpenTextDocumentParams, + ipythonMode = IPythonMode.None, + ) { + const doc = TextDocument.create( + params.textDocument.uri, + "sas", + params.textDocument.version, + params.textDocument.text, + ); + const languageService = this.sasLspProvider!(params.textDocument.uri); + const pythonDocContent = extractPythonCodes(doc, languageService); + const newParams: DidOpenTextDocumentParams = { ...params }; + newParams.textDocument = { ...params.textDocument }; + newParams.textDocument.languageId = "python"; + newParams.textDocument.text = pythonDocContent; + await super.onDidOpenTextDocument(newParams, ipythonMode); + } + + public async onDidCloseTextDocument(params: DidCloseTextDocumentParams) { + await super.onDidCloseTextDocument(params); + } + + public onDidChangeConfiguration(params: DidChangeConfigurationParams): void { + super.onDidChangeConfiguration(params); + } + + public async onDefinition( + params: TextDocumentPositionParams, + token: CancellationToken, + ): Promise { + return await super.onDefinition(params, token); + } + + public async onDeclaration( + params: TextDocumentPositionParams, + token: CancellationToken, + ): Promise { + return await super.onDeclaration(params, token); + } + + public async onTypeDefinition( + params: TextDocumentPositionParams, + token: CancellationToken, + ): Promise { + return await super.onTypeDefinition(params, token); + } + + public async onReferences( + params: ReferenceParams, + token: CancellationToken, + workDoneReporter: WorkDoneProgressReporter, + resultReporter: ResultProgressReporter | undefined, + ): Promise { + return await super.onReferences( + params, + token, + workDoneReporter, + resultReporter, + ); + } + + public async onDocumentSymbol( + params: DocumentSymbolParams, + token: CancellationToken, + ): Promise { + return await super.onDocumentSymbol(params, token); + } + + public async onWorkspaceSymbol( + params: WorkspaceSymbolParams, + token: CancellationToken, + resultReporter: ResultProgressReporter | undefined, + ): Promise { + return await super.onWorkspaceSymbol(params, token, resultReporter); + } + + public async onDocumentHighlight( + params: DocumentHighlightParams, + token: CancellationToken, + ): Promise { + if (params.position.character < 0) { + return null; + } + return await super.onDocumentHighlight(params, token); + } + + public async onSignatureHelp( + params: SignatureHelpParams, + token: CancellationToken, + ) { + return await super.onSignatureHelp(params, token); + } + + public async onCompletion( + params: CompletionParams, + token: CancellationToken, + ): Promise { + return await super.onCompletion(params, token); + } + + public async onCompletionResolve( + params: CompletionItem, + token: CancellationToken, + ): Promise { + return await super.onCompletionResolve(params, token); + } + + public async onPrepareRenameRequest( + params: PrepareRenameParams, + token: CancellationToken, + ) { + return await super.onPrepareRenameRequest(params, token); + } + + public async onRenameRequest(params: RenameParams, token: CancellationToken) { + if (params.position.character < 0) { + return null; + } + return await super.onRenameRequest(params, token); + } + + public async onCallHierarchyPrepare( + params: CallHierarchyPrepareParams, + token: CancellationToken, + ): Promise { + return await super.onCallHierarchyPrepare(params, token); + } + + public async onCallHierarchyIncomingCalls( + params: CallHierarchyIncomingCallsParams, + token: CancellationToken, + ) { + return await super.onCallHierarchyIncomingCalls(params, token); + } + + public async onCallHierarchyOutgoingCalls( + params: CallHierarchyOutgoingCallsParams, + token: CancellationToken, + ): Promise { + return await super.onCallHierarchyOutgoingCalls(params, token); + } + + public onDidChangeWatchedFiles(params: DidChangeWatchedFilesParams): void { + super.onDidChangeWatchedFiles(params); + } + + public async onExecuteCommand( + params: ExecuteCommandParams, + token: CancellationToken, + reporter: WorkDoneProgressReporter, + ) { + return await super.onExecuteCommand(params, token, reporter); + } + + public async executeCodeAction( + params: CodeActionParams, + token: CancellationToken, + ): Promise<(Command | CodeAction)[] | undefined | null> { + return await super.executeCodeAction(params, token); + } + + public async onShutdown(token: CancellationToken): Promise { + return await super.onShutdown(token); + } +} diff --git a/server/src/python/browser/fakeFileSystem.ts b/server/src/python/browser/fakeFileSystem.ts new file mode 100644 index 000000000..075adadc5 --- /dev/null +++ b/server/src/python/browser/fakeFileSystem.ts @@ -0,0 +1,79 @@ +// Copyright © 2022-2024, SAS Institute Inc., Cary, NC, USA. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +/* + * fakeFileSystem.ts + * + * simulate real fs access. + */ +import { CaseSensitivityDetector } from "pyright-internal-browser/dist/packages/pyright-internal/src/common/caseSensitivityDetector"; +import { ConsoleInterface } from "pyright-internal-browser/dist/packages/pyright-internal/src/common/console"; +import type { + FileSystem, + TempFile, + TmpfileOptions, +} from "pyright-internal-browser/dist/packages/pyright-internal/src/common/fileSystem"; +import { FileWatcherProvider } from "pyright-internal-browser/dist/packages/pyright-internal/src/common/fileWatcher"; +import { + directoryExists, + getDirectoryPath, +} from "pyright-internal-browser/dist/packages/pyright-internal/src/common/pathUtils"; +import { Uri } from "pyright-internal-browser/dist/packages/pyright-internal/src/common/uri/uri"; +import { UriEx } from "pyright-internal-browser/dist/packages/pyright-internal/src/common/uri/uriUtils"; +import { TestFileSystem } from "pyright-internal-browser/dist/packages/pyright-internal/src/tests/harness/vfs/filesystem"; + +import { typeShed } from "./typeShed"; + +const fs = new TestFileSystem(false, { cwd: "/" }); + +export function createFromRealFileSystem( + // eslint-disable-next-line @typescript-eslint/no-unused-vars + console?: ConsoleInterface, + // eslint-disable-next-line @typescript-eslint/no-unused-vars + fileWatcherProvider?: FileWatcherProvider, +): FileSystem { + // install builtin types + for (const entry of typeShed) { + const dir = getDirectoryPath(entry.filePath); + if (!directoryExists(fs, dir)) { + fs.mkdirpSync(dir); + } + fs.writeFileSync(UriEx.parse(entry.filePath), entry.content); + } + + return fs; +} + +export class RealTempFile implements TempFile, CaseSensitivityDetector { + private tmpDirPath: string = "pyright-tmp"; + private tmpFilePath: string = this.tmpDirPath + "/pyright-tmp-file"; + + constructor() { + // Empty + } + + tmpdir(): Uri { + if (!fs.existsSync(UriEx.parse(this.tmpDirPath))) { + fs.mkdirpSync(this.tmpDirPath); + } + return Uri.file(this.tmpDirPath, this); + } + + // eslint-disable-next-line @typescript-eslint/no-unused-vars + tmpfile(options?: TmpfileOptions): Uri { + const file = UriEx.parse(this.tmpFilePath); + if (!fs.existsSync(file)) { + fs.writeFileSync(file, ""); + } + return Uri.file(file.fileName, this); + } + + // eslint-disable-next-line @typescript-eslint/no-unused-vars + isCaseSensitive(uri: string): boolean { + // if (uri.startsWith(FileUriSchema)) { + // return this._isLocalFileSystemCaseSensitive(); + // } + + return true; + } +} diff --git a/server/src/python/browser/typeShed.ts b/server/src/python/browser/typeShed.ts new file mode 100644 index 000000000..3053cda7e --- /dev/null +++ b/server/src/python/browser/typeShed.ts @@ -0,0 +1,10 @@ +// Copyright © 2022-2024, SAS Institute Inc., Cary, NC, USA. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +// typeshed-loader will fill in real content when bundling +export const typeShed = [ + { + filePath: "", + content: "", + }, +]; diff --git a/server/src/python/browser/typeshed-loader/index.js b/server/src/python/browser/typeshed-loader/index.js new file mode 100644 index 000000000..7be34a0c7 --- /dev/null +++ b/server/src/python/browser/typeshed-loader/index.js @@ -0,0 +1,50 @@ +// Copyright © 2022-2024, SAS Institute Inc., Cary, NC, USA. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +/* eslint-disable @typescript-eslint/no-var-requires */ + +/** + * A webpack loader gathering typeshed for intellisense features + */ +const fs = require("fs/promises"); +const path = require("path"); + +const dirs = ["stdlib"]; +const result = []; + +async function* walk(dir) { + for await (const d of await fs.opendir(dir)) { + const entry = path.join(dir, d.name); + if (d.isDirectory()) { + yield* walk(entry); + } else if (d.isFile()) { + yield entry; + } + } +} + +async function loader() { + const callback = this.async(); + + for (const dir of dirs) { + const entry = path.resolve( + __dirname, + "../../../../node_modules/pyright-internal-node/dist/packages/pyright-internal/typeshed-fallback", + dir, + ); + const prefixLength = entry.indexOf("typeshed-fallback") - 1; + for await (const filename of walk(entry)) { + if (filename.endsWith(".pyi")) { + const content = await fs.readFile(filename); + result.push({ + content: content.toString(), + filePath: filename.slice(prefixLength).replace(/\\/g, "/"), + }); + } + } + } + + callback(null, `export const typeShed = ${JSON.stringify(result)}`); +} + +module.exports = loader; diff --git a/server/src/python/node/PyrightLanguageProviderNode.ts b/server/src/python/node/PyrightLanguageProviderNode.ts new file mode 100644 index 000000000..4db1cd926 --- /dev/null +++ b/server/src/python/node/PyrightLanguageProviderNode.ts @@ -0,0 +1,297 @@ +// Copyright © 2022-2024, SAS Institute Inc., Cary, NC, USA. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +import { + CallHierarchyIncomingCallsParams, + CallHierarchyItem, + CallHierarchyOutgoingCall, + CallHierarchyOutgoingCallsParams, + CallHierarchyPrepareParams, + CancellationToken, + CodeAction, + CodeActionParams, + Command, + CompletionItem, + CompletionList, + CompletionParams, + Connection, + Declaration, + DeclarationLink, + Definition, + DefinitionLink, + DidChangeConfigurationParams, + DidChangeWatchedFilesParams, + DidCloseTextDocumentParams, + DidOpenTextDocumentParams, + DocumentHighlight, + DocumentHighlightParams, + DocumentSymbol, + DocumentSymbolParams, + ExecuteCommandParams, + HoverParams, + InitializeParams, + InitializeResult, + LSPAny, + Location, + PrepareRenameParams, + ReferenceParams, + RenameParams, + ResultProgressReporter, + SignatureHelpParams, + SymbolInformation, + TextDocumentPositionParams, + WorkDoneProgressReporter, + WorkspaceSymbol, + WorkspaceSymbolParams, +} from "vscode-languageserver"; +import { TextDocument } from "vscode-languageserver-textdocument"; + +import { IPythonMode } from "pyright-internal-node/dist/packages/pyright-internal/src/analyzer/sourceFile"; +import { + FileSystem, + ReadOnlyFileSystem, +} from "pyright-internal-node/dist/packages/pyright-internal/src/common/fileSystem"; +import { ClientCapabilities } from "pyright-internal-node/dist/packages/pyright-internal/src/common/languageServerInterface"; +import { DocumentRange } from "pyright-internal-node/dist/packages/pyright-internal/src/common/textRange"; +import { Uri } from "pyright-internal-node/dist/packages/pyright-internal/src/common/uri/uri"; +import { CollectionResult } from "pyright-internal-node/dist/packages/pyright-internal/src/languageService/documentSymbolCollector"; +import { ParseFileResults } from "pyright-internal-node/dist/packages/pyright-internal/src/parser/parser"; +import { PyrightServer } from "pyright-internal-node/dist/packages/pyright-internal/src/server"; + +import { LanguageServiceProvider } from "../../sas/LanguageServiceProvider"; +import { extractPythonCodes } from "../utils"; + +export class PyrightLanguageProviderNode extends PyrightServer { + protected sasLspProvider?: (uri: string) => LanguageServiceProvider; + + constructor( + connection: Connection, + maxWorkers: number, + realFileSystem?: FileSystem, + ) { + // eslint-disable-next-line @typescript-eslint/consistent-type-assertions + super({ ...connection, listen() {} } as LSPAny, maxWorkers, realFileSystem); + } + + public setSasLspProvider( + provider: (uri: string) => LanguageServiceProvider, + ): void { + this.sasLspProvider = provider; + } + + public getClientCapabilities(): ClientCapabilities { + return this.client; + } + + protected setupConnection( + // eslint-disable-next-line @typescript-eslint/no-unused-vars + supportedCommands: string[], + // eslint-disable-next-line @typescript-eslint/no-unused-vars + supportedCodeActions: string[], + ): void { + return; + } + + public onInitialized(): void { + this.updateSettingsForAllWorkspaces(); + super.onInitialized(); + } + + public async initialize( + params: InitializeParams, + supportedCommands: string[], + supportedCodeActions: string[], + ): Promise { + return super.initialize(params, supportedCommands, supportedCodeActions); + } + + public addContentChange(doc: TextDocument): void { + const languageService = this.sasLspProvider!(doc.uri); + const pythonDoc = extractPythonCodes(doc, languageService); + + this.onDidChangeTextDocument({ + textDocument: doc, + contentChanges: [{ text: pythonDoc }], + }); + } + + public async onHover(params: HoverParams, token: CancellationToken) { + return await super.onHover(params, token); + } + + public async onDidOpenTextDocument( + params: DidOpenTextDocumentParams, + ipythonMode = IPythonMode.None, + ) { + const doc = TextDocument.create( + params.textDocument.uri, + "sas", + params.textDocument.version, + params.textDocument.text, + ); + const languageService = this.sasLspProvider!(params.textDocument.uri); + const pythonDocContent = extractPythonCodes(doc, languageService); + const newParams: DidOpenTextDocumentParams = { ...params }; + newParams.textDocument = { ...params.textDocument }; + newParams.textDocument.languageId = "python"; + newParams.textDocument.text = pythonDocContent; + await super.onDidOpenTextDocument(newParams, ipythonMode); + } + + public async onDidCloseTextDocument(params: DidCloseTextDocumentParams) { + await super.onDidCloseTextDocument(params); + } + + public onDidChangeConfiguration(params: DidChangeConfigurationParams): void { + super.onDidChangeConfiguration(params); + } + + public async onDefinition( + params: TextDocumentPositionParams, + token: CancellationToken, + ): Promise { + return await super.onDefinition(params, token); + } + + public async onDeclaration( + params: TextDocumentPositionParams, + token: CancellationToken, + ): Promise { + return await super.onDeclaration(params, token); + } + + public async onTypeDefinition( + params: TextDocumentPositionParams, + token: CancellationToken, + ): Promise { + return await super.onTypeDefinition(params, token); + } + + public async onReferences( + params: ReferenceParams, + token: CancellationToken, + workDoneReporter: WorkDoneProgressReporter, + resultReporter: ResultProgressReporter | undefined, + createDocumentRange?: ( + uri: Uri, + result: CollectionResult, + parseResults: ParseFileResults, + ) => DocumentRange, + convertToLocation?: ( + fs: ReadOnlyFileSystem, + ranges: DocumentRange, + ) => Location | undefined, + ): Promise { + return await super.onReferences( + params, + token, + workDoneReporter, + resultReporter, + createDocumentRange, + convertToLocation, + ); + } + + public async onDocumentSymbol( + params: DocumentSymbolParams, + token: CancellationToken, + ): Promise { + return await super.onDocumentSymbol(params, token); + } + + public async onWorkspaceSymbol( + params: WorkspaceSymbolParams, + token: CancellationToken, + resultReporter: ResultProgressReporter | undefined, + ): Promise { + return await super.onWorkspaceSymbol(params, token, resultReporter); + } + + public async onDocumentHighlight( + params: DocumentHighlightParams, + token: CancellationToken, + ): Promise { + if (params.position.character < 0) { + return null; + } + return await super.onDocumentHighlight(params, token); + } + + public async onSignatureHelp( + params: SignatureHelpParams, + token: CancellationToken, + ) { + return await super.onSignatureHelp(params, token); + } + + public async onCompletion( + params: CompletionParams, + token: CancellationToken, + ): Promise { + return await super.onCompletion(params, token); + } + + public async onCompletionResolve( + params: CompletionItem, + token: CancellationToken, + ): Promise { + return await super.onCompletionResolve(params, token); + } + + public async onPrepareRenameRequest( + params: PrepareRenameParams, + token: CancellationToken, + ) { + return await super.onPrepareRenameRequest(params, token); + } + + public async onRenameRequest(params: RenameParams, token: CancellationToken) { + if (params.position.character < 0) { + return null; + } + return await super.onRenameRequest(params, token); + } + + public async onCallHierarchyPrepare( + params: CallHierarchyPrepareParams, + token: CancellationToken, + ): Promise { + return await super.onCallHierarchyPrepare(params, token); + } + + public async onCallHierarchyIncomingCalls( + params: CallHierarchyIncomingCallsParams, + token: CancellationToken, + ) { + return await super.onCallHierarchyIncomingCalls(params, token); + } + + public async onCallHierarchyOutgoingCalls( + params: CallHierarchyOutgoingCallsParams, + token: CancellationToken, + ): Promise { + return await super.onCallHierarchyOutgoingCalls(params, token); + } + + public onDidChangeWatchedFiles(params: DidChangeWatchedFilesParams): void { + super.onDidChangeWatchedFiles(params); + } + + public async onExecuteCommand( + params: ExecuteCommandParams, + token: CancellationToken, + reporter: WorkDoneProgressReporter, + ) { + return await super.onExecuteCommand(params, token, reporter); + } + + public async onShutdown(token: CancellationToken): Promise { + return await super.onShutdown(token); + } + + public async executeCodeAction( + params: CodeActionParams, + token: CancellationToken, + ): Promise<(Command | CodeAction)[] | undefined | null> { + return await super.executeCodeAction(params, token); + } +} diff --git a/server/src/python/sas/sas2py.pyi b/server/src/python/sas/sas2py.pyi new file mode 100644 index 000000000..2797e1ce4 --- /dev/null +++ b/server/src/python/sas/sas2py.pyi @@ -0,0 +1,211 @@ +# Copyright © 2024, SAS Institute Inc., Cary, NC, USA. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 + +class SAS2py: + """ + This module provides the interface from the Python process to Proc Python in + the SAS process. It provides user callable methods for interacting with the + SAS process it's attached to. + """ + + workpath: str + """string containing the WORK libref's filesystem path, including the trailing slash.""" + + def hideLOG(self, tf: bool) -> None: + """ + This methods identifies whether the SAS LOG output for the data transfer methods, sd2df() + and df2sd() is written or cached to a file. Since it's the Python log, and there's a lot + of SAS code and messages that those methods generate, the default is to not see all + of that cluttering up the Python log you're seeing. But, for diagnosing problems, having + that shown can be helpful. When set to True (default), the contents of the SAS LOG for + those methods is cached, and you can see it at any time using the printLOG() method, or + clear the current contents of the cache file using clearLOG(). + + :param tf: boolean default True. Whether to hide the LOG output for data transfer routines + + :return: None + """ + + def printLOG(self, method='SAS') -> None: + """ + This methods renders the parts of the SAS LOG output that were hidden from the data + transfer routines sd2df() and df2sd(), based upon the setting of hideLOG() which + defaults to True. + + :param method: the default value 'SAS' uses SAS to write the output to the SAS LOG which allows + for proper coloring of the output in Studio and also shows up before the Python + output when in 'submit' processing. + The value 'Python' uses Python to write the output to the Python log, which + shows up in the SAS LOG without coloring and in the Python output where the method + was executed, regardless of using 'submit' or 'interactive'. + + + :return: None + """ + + def clearLOG(self) -> None: + """ + This method will delete all of the currently cached LOG output, if any. Subsequent output + from data transfer methods will continue to cache their output, if set to hide that output, + based upon the setting of hideLOG() + + :return: None + """ + + def submit(self, code: str) -> int: + """ + This methods submits the code you provide back into the existing SAS session, recursively + executing it while still within the PROC PYTHON that is running this method. + + :param code: string of SAS code to submit + + :return: None + """ + + def symget(self, name: str) -> str: + """ + This methods retrieves the value for the macro variable who's name you provided. It returns + the string value. If the value represents a numeric type, you can simply cast it to what type + you like. + + :param name: string of SAS macro variable name + + :return: str + """ + + def symput(self, name: str, val: str) -> int: + """ + This methods assigns a macro variable in SAS with the name and value you provide. + + :param name: string of SAS macro variable name + :param val: value to assign, which will be converted to a string, as that's what macro + variable in SAS are + + :return: int + """ + + def pyplot(self, plot: object, filename: str = None, filepath: str = None, + filetype: str='svg', **kwargs) -> None: + """ + This methods renders a matplot.pyplot object, or other plot object that supports pyplot's + savefig() method. It simply calls savefig() writing the plot to one of the supported ODS types + and submits the SAS code to render that file using ODS. + + :param plot: the plot object; pyplot or equivalent supporting the same savefig() method + :param filename: name of the file to create, defaults to 'matplot' + :param filepath: directory path to write the file; defaults to the work library + :param filetype: file type to create; defaults to 'svg'. This is passed to savefig via format=filetype + :param kwargs: kwargs passed to the savefig() method + + :return: None + """ + + def renderImage(self, filename: str) -> None: + """ + This method renders a plot that has already been written to a file, so you + can render plots from any plotting object that isn't pyplot or doesn't have the same + savefig() method as pyplot. You write the plot to a supported ODS file type using that + objects methods and just call this method to have it rendered. + + :param filename: fully qualified name of the file to render. + + :return: None + """ + + def logMessage(self, message: str, messageType: str = 'NOTE') -> None: + """ + Writes a well formed message to the SAS Log + + :param message: {String} - Message that should be written to the SAS log + :param messageType: {String - default: NOTE} + - NOTE, writes a Note to the SAS log + - WARNING, writes a Warning to the SAS log + - ERROR, writes an Error to the SAS log + + :return: None + + # Example usage + SAS.logMessage('test') + SAS.logMessage('testWarn', 'warning') + SAS.logMessage('testError', 'error') + + """ + + def sasdata2dataframe(self, dataset: str, rowsep: str = '\x01', colsep: str = '\x02', + rowrep: str = ' ', colrep: str = ' ', **kwargs): + """ + See the doc for sd2df(). This is just an alias for that method + """ + + def sd2df(self, dataset: str, rowsep: str = '\x01', colsep: str = '\x02', + rowrep: str = ' ', colrep: str = ' ', **kwargs): + """ + This method exports the SAS Data Set to a Pandas DataFrame, returning the DataFrame object. + + :param dataset: the 'libref.table(optional dataset options)' name of the SAS Data Set + + These parameters are not to be used normally. Don't use them without instruction. + + :param rowsep: the row separator character to use; defaults to hex(1) + :param colsep: the column separator character to use; defaults to hex(2) + :param rowrep: the char to convert to for any embedded rowsep chars, defaults to ' ' + :param colrep: the char to convert to for any embedded colsep chars, defaults to ' ' + :param errors: this is the parameter to decode(errors=) when reading the stream of data into pandas and converting + from bytes to chars. If the variables in the SAS data set have invalid characters (from truncation or other) + then you can provide values like 'replace' or 'ignore' to load the invalid data instead of failing. + :param kwargs: these are for internal use and are generally NOT needed. + + :return: Pandas DataFrame + """ + + def dataframe2sasdata(self, df, dataset: str, + LF: str = '\x01', CR: str = '\x02', + colsep: str = '\x03', colrep: str = ' ', + datetimes: dict={}, outfmts: dict={}, + labels: dict={}, char_lengths: dict={}, **kwargs): + """ + See the doc for df2sd(). This is just an alias for that method + """ + + def df2sd(self, df: 'pandas.DataFrame', dataset: str, + LF: str = '\x01', CR: str = '\x02', + colsep: str = '\x03', colrep: str = ' ', + datetimes: dict={}, outfmts: dict={}, + labels: dict={}, char_lengths: dict={}, **kwargs) -> int: + """ + This method imports a Pandas DataFrame to a SAS Data Set you identify via the `dataset` parameter (libref.table). + + Also note that DataFrame indexes (row label) are not transferred over as columns, as they aren't actually in df.columns. + You can simply use df.reset_index() before this method and df.set_index() after to have the index be a column which + is transferred over to the SAS data set. If you want to create a SAS index at the same time, specify that with the + output dataset options. + + :param df: Pandas DataFrame to import to a SAS Data Set + :param dataset: the 'libref.table(optional output data set options)' name of the SAS Data Set to create + :param datetimes: dict with column names as keys and values of 'date' or 'time' to create SAS date or times instead of datetimes + :param outfmts: dict with column names and SAS formats to assign to the new SAS data set + :param labels: dict with column names and labels to assign to the new SAS data set + :param char_lengths: a dictionary containing the names:lengths of all of the character columns. This eliminates + running the code to calculate the lengths, and goes straight to transferring the data + + These parameters are not to be used normally. Don't use them without instruction. + + :param LF: the character to use for LF when transferring the data; defaults to hex(1) + :param CR: the character to use for CR when transferring the data; defaults to hex(2) + :param colsep: the column separator character used for streaming the delimited data to SAS defaults to hex(3) + :param colrep: the char to convert to for any embedded colsep, LF, CR chars in the data; defaults to ' ' + + :return: int + """ + + def sasfnc(self, *arg) -> str: + """ + This method executes the SAS or FCMP function you provide, returning the results. + The parameters vary based upon the function being called. But the first parameter + is the name of the function, followed by the required parameters for that function. + + :param arg[1]: name of the SAS function to call + :param arg[2] to arg[n]: arguments to SAS function + + :return: str + """ diff --git a/server/src/python/utils.ts b/server/src/python/utils.ts new file mode 100644 index 000000000..aeaf273b3 --- /dev/null +++ b/server/src/python/utils.ts @@ -0,0 +1,81 @@ +// Copyright © 2022-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"; + +export const extractPythonCodes = ( + doc: TextDocument, + languageService: LanguageServiceProvider, +): string => { + const codeZoneManager = languageService.getCodeZoneManager(); + let pythonDocLines = ["import sas2py;SAS = sas2py.SAS2py() #type: ignore"]; + 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 PYTHON") { + continue; + } + let pythonCodeStart = undefined; + let pythonCodeEnd = undefined; + const pos = { ...symbol.range.start }; + while (pos.line <= symbol.range.end.line) { + if ( + !pythonCodeStart && + codeZoneManager.getCurrentZone(pos.line, pos.character) === + CodeZoneManager.ZONE_TYPE.EMBEDDED_LANG + // // a codezone bug + // pos.line >= symbol.range.start.line + 2 + ) { + pythonCodeStart = { ...pos }; + } + if ( + pythonCodeStart && + codeZoneManager.getCurrentZone(pos.line, pos.character) !== + CodeZoneManager.ZONE_TYPE.EMBEDDED_LANG + ) { + pythonCodeEnd = { ...pos }; + if (pythonCodeEnd.character === 0) { + pythonCodeEnd.line--; + pythonCodeEnd.character = + languageService.model.getColumnCount(pythonCodeEnd.line) - 1; + if (pythonCodeEnd.character < 0) { + pythonCodeEnd.character = 0; + } + } + break; + } + pos.character++; + if (pos.character >= languageService.model.getLine(pos.line).length) { + pos.line++; + pos.character = 0; + } + } + if (!pythonCodeStart) { + continue; + } + const pythonCodeLines = doc + .getText({ + start: pythonCodeStart, + end: pythonCodeEnd ?? symbol.range.end, + }) + .split("\n"); + const lineGap = pythonCodeStart.line - pythonDocLines.length; + if (lineGap > 0) { + pythonDocLines = pythonDocLines.concat(Array(lineGap).fill("")); + } else if (lineGap === -1 && pythonCodeLines[0] === "") { + // head in one line: proc python; submit; + pythonCodeLines.shift(); + } + pythonDocLines = pythonDocLines.concat(pythonCodeLines); + pythonDocLines.push("pass"); + } + const pythonDoc = pythonDocLines.join("\n"); + return pythonDoc; +}; diff --git a/server/src/sas/CodeZoneManager.ts b/server/src/sas/CodeZoneManager.ts index 9531f92f6..de4dd71e9 100644 --- a/server/src/sas/CodeZoneManager.ts +++ b/server/src/sas/CodeZoneManager.ts @@ -90,6 +90,7 @@ export class CodeZoneManager { private _stmtName = ""; private _optName = ""; private _subOptName = ""; + private _embeddedCodeStarted = false; private _topZone = 0; private _sectionMode: { @@ -122,6 +123,7 @@ export class CodeZoneManager { this._stmtName = ""; this._optName = ""; this._subOptName = ""; + this._embeddedCodeStarted = false; } private _needOptionDelimiter() { if (!this._stmtWithOptionDelimiter[this._procName]) { @@ -249,57 +251,56 @@ export class CodeZoneManager { } private _token(line: number, col: number): TokenEx | null { let syntax = this._syntaxProvider.getSyntax(line); - const len = syntax.length; - let i = 1, - j = -1, + let foundSyntaxIdx = -1, type: Token["type"] = "text", currLine = line; - for (; i < len; i++) { - if (syntax[i].start >= col) { - if (syntax[i - 1].start <= col) { - j = i - 1; - type = syntax[j].style; + + for (let i = 0; i < syntax.length; i++) { + if (syntax[i].start <= col) { + if (i === syntax.length - 1 || syntax[i + 1].start > col) { + foundSyntaxIdx = i; + type = syntax[i].style; break; - } else { - break; //not found, not continue } } } if (Lexer.isComment[type] || Lexer.isLiteral[type]) { while (currLine >= 0) { - if (syntax[j].state instanceof Object) { + if (syntax[foundSyntaxIdx].state instanceof Object) { return { type: type, text: "", line: currLine, - col: syntax[j].start, - endLine: syntax[j].state.line, - endCol: syntax[j].state.col, + col: syntax[foundSyntaxIdx].start, + endLine: syntax[foundSyntaxIdx].state.line, + endCol: syntax[foundSyntaxIdx].state.col, }; - } else if (syntax[j].state === 1) { + } else if (syntax[foundSyntaxIdx].state === 1) { //met the start of the node break; } else { - j--; - if (j < 0) { + foundSyntaxIdx--; + if (foundSyntaxIdx < 0) { do { currLine--; syntax = this._syntaxProvider.getSyntax(currLine); //skip the line without syntax } while (currLine >= 0 && syntax.length === 0); - j = syntax.length - 1; + foundSyntaxIdx = syntax.length - 1; } } } } - if (i > 0) { + if (foundSyntaxIdx >= 0) { const lineText = this._model.getLine(line); let endCol = 0, startCol = 0; syntax = this._syntaxProvider.getSyntax(line); if (syntax.length !== 0) { - endCol = syntax[i] ? syntax[i].start : lineText.length; - startCol = syntax[i - 1].start; + endCol = syntax[foundSyntaxIdx + 1] + ? syntax[foundSyntaxIdx + 1].start + : lineText.length; + startCol = syntax[foundSyntaxIdx].start; } return { type: type, @@ -318,8 +319,7 @@ export class CodeZoneManager { text = "", col = 0, type: Token["type"] = "text", - i = 0, - token = null; + i = 0; const lineCount = this._model.getLineCount(); if (context.line >= lineCount) { @@ -355,26 +355,23 @@ export class CodeZoneManager { if ( /*syntax[syntaxLen-1].state !== 0 && */ // for the line without normal end mark. syntax[syntaxLen - 1].start <= context.col && - lineLen >= context.col + lineLen >= context.col && + syntax[syntaxLen - 1].start < lineLen // to prevent "" ) { i = syntaxLen - 1; } else { for (i = context.syntaxIdx; i >= 0; i--) { if ( syntax[i].start <= context.col && - syntax[i + 1].start >= context.col + syntax[i + 1].start >= context.col && + syntax[i].start !== syntax[i + 1].start // to prevent "" ) { break; } } } - // get the text and type - if (context.syntaxIdx < 0 || i < 0) { - //this line is special, no normal end mark, - col = 0; - type = syntax[0].style; - text = line.substring(0, lineLen); - } else { + + if (i >= 0) { col = syntax[i].start; type = syntax[i].style; text = line.substring( @@ -382,18 +379,22 @@ export class CodeZoneManager { syntax[i + 1] ? syntax[i + 1].start : lineLen, ); } + // adjust pointer - if (i < 1) { - context.col = -1; - context.syntaxIdx = -1; - } else { + if (i >= 1) { context.col = syntax[i - 1].start; context.syntaxIdx = i - 1; + } else { + context.col = -1; + context.syntaxIdx = -1; } } while (/^\s*$/.test(text)); if (Lexer.isComment[type] || Lexer.isLiteral[type]) { - token = this._token(context.line, col + 1)!; + const token = this._token(context.line, col)!; + if (!token) { + return null; + } if ( token.endLine && (token.line !== context.line || token.col !== context.col) @@ -692,9 +693,6 @@ export class CodeZoneManager { len = 0; const tokens = []; - const initLine = context.line; - const initCol = context.col; - context.syntaxIdx = -1; context.tokens = null; while (true) { @@ -1136,14 +1134,19 @@ export class CodeZoneManager { const zone = this._stmtEx(context, stmt); type = zone.type; if (["PYTHON", "LUA"].includes(this._procName)) { - if ( - ["SUBMIT", "ENDSUBMIT", "INTERACTIVE", "ENDINTERACTIVE"].includes( - stmt.text, - ) - ) { + if (!this._embeddedCodeStarted) { + if (["SUBMIT", "INTERACTIVE", "I"].includes(stmt.text)) { + this._embeddedCodeStarted = true; + } return CodeZoneManager.ZONE_TYPE.PROC_STMT; } else { - return CodeZoneManager.ZONE_TYPE.EMBEDDED_LANG; + // this._embeddedCodeStarted is true + if (["ENDSUBMIT", "ENDINTERACTIVE", "RUN"].includes(stmt.text)) { + this._embeddedCodeStarted = false; + return CodeZoneManager.ZONE_TYPE.PROC_STMT; + } else { + return CodeZoneManager.ZONE_TYPE.EMBEDDED_LANG; + } } } else if (this._isCall(zone)) { type = CodeZoneManager.ZONE_TYPE.RESTRICTED; @@ -2456,8 +2459,11 @@ export class CodeZoneManager { } } private _currentZone(line: number, col: number) { - const newToken = this._token(line, col)!, - type = newToken.type; //self.type(line,col), + const newToken = this._token(line, col)!; + const type = newToken.type; + if (type === "embedded-code") { + return CodeZoneManager.ZONE_TYPE.EMBEDDED_LANG; + } let context = null, pos: any = this._normalize(line, col - 1); const tmpLine = pos.line, diff --git a/server/src/sas/CompletionProvider.ts b/server/src/sas/CompletionProvider.ts index f27e20be4..f24cec08a 100644 --- a/server/src/sas/CompletionProvider.ts +++ b/server/src/sas/CompletionProvider.ts @@ -612,9 +612,7 @@ export class CompletionProvider { }); } - getCompleteItems( - position: Position, - ): Promise { + getCompleteItems(position: Position): Promise { return new Promise((resolve) => { this._getZone(position); const prefix = this.popupContext.prefix; @@ -686,20 +684,36 @@ export class CompletionProvider { getCompleteItemHelp(item: CompletionItem): Promise { return new Promise((resolve) => { - this._loadHelp({ - keyword: item.label, - type: "tooltip", - ...this.popupContext, - cb: (data) => { - if (data && data.data) { - item.documentation = { - kind: MarkupKind.Markdown, - value: this._addLinkContext(this.popupContext.zone, data), - }; - } - resolve(item); - }, - }); + if (["endsubmit", "endinteractive"].includes(item.label?.toLowerCase())) { + this.loader.getProcedureStatementHelp( + "PYTHON", + item.label.toUpperCase(), + (data) => { + if (data && data.data) { + item.documentation = { + kind: MarkupKind.Markdown, + value: this._addLinkContext(515, data), + }; + } + resolve(item); + }, + ); + } else { + this._loadHelp({ + keyword: item.label, + type: "tooltip", + ...this.popupContext, + cb: (data) => { + if (data && data.data) { + item.documentation = { + kind: MarkupKind.Markdown, + value: this._addLinkContext(this.popupContext.zone, data), + }; + } + resolve(item); + }, + }); + } }); } @@ -1493,6 +1507,10 @@ export class CompletionProvider { contextText = getText("ce_ac_data_step_txt"); linkTail = "%22DATA+STEP%22+%22" + keyword + "+" + "STATEMENT%22"; break; + case ZONE_TYPE.DATA_STEP_STMT_OPT: + contextText = getText("ce_ac_statement.fmt", context.stmtName); + linkTail = "%22" + context.stmtName + "+STATEMENT%22+" + keyword; + break; case ZONE_TYPE.DATA_STEP_OPT_NAME: case ZONE_TYPE.DATA_STEP_DEF_OPT: contextText = getText("ce_ac_data_step_txt"); diff --git a/server/src/sas/LanguageServiceProvider.ts b/server/src/sas/LanguageServiceProvider.ts index 70e936923..f4565e695 100644 --- a/server/src/sas/LanguageServiceProvider.ts +++ b/server/src/sas/LanguageServiceProvider.ts @@ -1,8 +1,11 @@ // Copyright © 2022-2023, SAS Institute Inc., Cary, NC, USA. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import { FoldingRange } from "vscode-languageserver"; +import { + DocumentSymbol, + FoldingRange, + SymbolKind, +} from "vscode-languageserver"; import { Range, TextDocument } from "vscode-languageserver-textdocument"; -import { DocumentSymbol, SymbolKind } from "vscode-languageserver-types"; import { CodeZoneManager } from "./CodeZoneManager"; import { CompletionProvider } from "./CompletionProvider"; @@ -52,8 +55,8 @@ const SymbolKinds = [ ]; export class LanguageServiceProvider { - private model; - private syntaxProvider; + public model; + public syntaxProvider; public completionProvider; public formatOnTypeProvider; public formatter; @@ -167,7 +170,7 @@ export class LanguageServiceProvider { end: { line: block.endFoldingLine, character: block.endFoldingCol }, }; const docSymbol: DocumentSymbol = { - name: block.type === 1 ? this._getProcName(block.startLine) : block.name, + name: this._getSymbolName(block), kind: SymbolKinds[block.type], range, selectionRange: range, @@ -244,26 +247,31 @@ export class LanguageServiceProvider { return this.syntaxProvider.lexer.syntaxDb.setLibService(fn); } - private _getProcName(line: number) { + private _getSymbolName(block: FoldingBlock) { + const line = block.startLine; const tokens = this.syntaxProvider.getSyntax(line); - for (let i = 0; i < tokens.length; i++) { + for (let i = 2; i < tokens.length; i++) { const token = tokens[i]; - if (token.style === "proc-name") { + if (token.start <= block.startCol) { + continue; + } + if (token.style === "proc-name" || token.style === "text") { const end = i === tokens.length - 1 ? this.model.getColumnCount(line) : tokens[i + 1].start; - return ( - "PROC " + - this.model - .getText({ - start: { line, column: token.start }, - end: { line, column: end }, - }) - .toUpperCase() - ); + const tokenText = this.model.getText({ + start: { line, column: token.start }, + end: { line, column: end }, + }); + if (tokenText.trim() === "") { + continue; + } + return `${block.name} ${ + token.style === "proc-name" ? tokenText.toUpperCase() : tokenText + }`; } } - return ""; + return block.name; } } diff --git a/server/src/sas/Lexer.ts b/server/src/sas/Lexer.ts index f78490cb2..12c2e1d3c 100644 --- a/server/src/sas/Lexer.ts +++ b/server/src/sas/Lexer.ts @@ -304,7 +304,7 @@ export class Lexer { } if ( token.type === "text" && - ["SUBMIT", "INTERACTIVE"].includes(token.text) + ["SUBMIT", "INTERACTIVE", "I"].includes(token.text) ) { this.context.embeddedLangState = EmbeddedLangState.PROC_PYTHON_SUBMIT_OR_INTERACTIVE; @@ -318,7 +318,7 @@ export class Lexer { } if ( token.type === "text" && - ["SUBMIT", "INTERACTIVE"].includes(token.text) + ["SUBMIT", "INTERACTIVE", "I"].includes(token.text) ) { this.context.embeddedLangState = EmbeddedLangState.PROC_LUA_SUBMIT_OR_INTERACTIVE; @@ -358,7 +358,7 @@ export class Lexer { const stringReg = /'''|"""|("[^"]*?("|$))|('[^']*?('|$))/; const commentReg = /#.*$/; const secReg = - /(\b((endsubmit|endinteractive)(\s+|\/\*.*?\*\/)*;|(data|proc|%macro)\b[^'";]*;)(\s+|\/\*.*?\*\/)*$)/; + /(\b((endsubmit|endinteractive)(\s+|\/\*.*?\*\/)*;|(data|proc|%macro)\b[^'";]*;))/; match = new RegExp( `${stringReg.source}|${commentReg.source}|${secReg.source}`, "m", @@ -376,7 +376,7 @@ export class Lexer { } else { token = this._foundEmbeddedCodeToken(this.curr, { line: line, - column: match.index, + column: pos + match.index, }); break SWITCH; } @@ -429,7 +429,7 @@ export class Lexer { const stringReg = /("[^"]*("|$))|('[^']*('|$))|\[\[/; const commentReg = /--[^[].*$|--\[\[|\/\*/; const secReg = - /(\b((endsubmit|endinteractive)(\s+|\/\*.*?\*\/)*;|(data|proc|%macro)\b[^'";]*;)(\s+|\/\*.*?\*\/)*$)/; + /(\b((endsubmit|endinteractive)(\s+|\/\*.*?\*\/)*;|(data|proc|%macro)\b[^'";]*;))/; const reg = new RegExp( `${stringReg.source}|${commentReg.source}|${secReg.source}`, "m", @@ -452,7 +452,7 @@ export class Lexer { } else { token = this._foundEmbeddedCodeToken(this.curr, { line: line, - column: match.index, + column: pos + match.index, }); break SWITCH; } @@ -987,6 +987,7 @@ export class Lexer { this.curr.line = line; this.curr.column = col; this.context.lastNoncommentToken = null; + this.context.embeddedLangState = EmbeddedLangState.NONE; this.quoting = -1; this.bquoting = -1; this.ignoreFormat = false; diff --git a/server/src/sas/LexerEx.ts b/server/src/sas/LexerEx.ts index c330b4f23..425cc1bb6 100644 --- a/server/src/sas/LexerEx.ts +++ b/server/src/sas/LexerEx.ts @@ -6,7 +6,12 @@ import { Lexer, Token } from "./Lexer"; import { Model } from "./Model"; import { SyntaxDataProvider } from "./SyntaxDataProvider"; import { Change } from "./SyntaxProvider"; -import { TextPosition, arrayToMap } from "./utils"; +import { + TextPosition, + arrayToMap, + isCustomRegionEndComment, + isCustomRegionStartComment, +} from "./utils"; /** * LexerEx to handle basic semantic related problems. @@ -1700,14 +1705,14 @@ export class LexerEx { } private isCustomBlockStart_(token: Token): boolean { - if (token && /^\s*[%/]?\*\s*region\b/i.test(token.text)) { + if (token && isCustomRegionStartComment(token.text)) { return true; } return false; } private isCustomBlockEnd_(token: Token): boolean { - if (token && /^\s*[%/]?\*\s*endregion\b/i.test(token.text)) { + if (token && isCustomRegionEndComment(token.text)) { return true; } return false; @@ -3095,7 +3100,7 @@ export class LexerEx { generalProcStmt = false; } } else if (procName === "LUA" || procName === "PYTHON") { - if (["SUBMIT", "INTERACTIVE"].includes(word)) { + if (["SUBMIT", "INTERACTIVE", "I"].includes(word)) { const next = this.prefetch_({ pos: 1 }); if (next && next.text === ";" && next.type === "sep") { this.stack.push({ diff --git a/server/src/sas/formatter/printer.ts b/server/src/sas/formatter/printer.ts index 55af1b745..eb1231c61 100644 --- a/server/src/sas/formatter/printer.ts +++ b/server/src/sas/formatter/printer.ts @@ -45,7 +45,12 @@ export const print: Printer["print"] = (path, options, print) => { ); if (node.type === "region") { const region = printRegion(children); - return !path.isFirst && node.block ? [hardline, ...region] : region; + return !( + path.isFirst || + (path.index === 1 && path.parent?.type === "region") + ) && node.block + ? [hardline, ...region] + : region; } return [...join(line, children), literalline]; } diff --git a/server/src/sas/utils.ts b/server/src/sas/utils.ts index b99fc40d2..c1477aada 100644 --- a/server/src/sas/utils.ts +++ b/server/src/sas/utils.ts @@ -103,3 +103,11 @@ export function getText(key: string, arg?: string): string { } return result; } + +export const isCustomRegionStartComment = (commmentText?: string) => { + return /^\s*[%/]?\*\s*region\b/i.test(commmentText ?? ""); +}; + +export const isCustomRegionEndComment = (commmentText?: string) => { + return /^\s*[%/]?\*\s*endregion\b/i.test(commmentText ?? ""); +}; diff --git a/server/src/server.ts b/server/src/server.ts index 1db45f264..e65176d15 100644 --- a/server/src/server.ts +++ b/server/src/server.ts @@ -1,30 +1,65 @@ -// Copyright © 2022-2023, SAS Institute Inc., Cary, NC, USA. All Rights Reserved. +// Copyright © 2022-2024, SAS Institute Inc., Cary, NC, USA. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 import { + CancellationToken, + CodeActionKind, + CompletionItemKind, + CompletionTriggerKind, Connection, + DidChangeConfigurationParams, + DidChangeWatchedFilesParams, + DocumentHighlightParams, + DocumentSymbol, + ExecuteCommandParams, InitializeResult, + Location, + Position, + PrepareRenameParams, + Range, + ReferenceParams, + Registration, + RegistrationRequest, + RenameParams, + ResultProgressReporter, SemanticTokensRequest, + TextDocumentPositionParams, TextDocumentSyncKind, - TextDocuments, + Unregistration, + UnregistrationRequest, + WorkDoneProgressReporter, + WorkspaceSymbolParams, } from "vscode-languageserver"; import { TextDocument } from "vscode-languageserver-textdocument"; -import { CompletionProvider } from "./sas/CompletionProvider"; +import { ReadOnlyFileSystem } from "pyright-internal-node/dist/packages/pyright-internal/src/common/fileSystem"; +import { DocumentRange } from "pyright-internal-node/dist/packages/pyright-internal/src/common/textRange"; +import { Uri } from "pyright-internal-node/dist/packages/pyright-internal/src/common/uri/uri"; +import { CollectionResult } from "pyright-internal-node/dist/packages/pyright-internal/src/languageService/documentSymbolCollector"; +import { ParseFileResults } from "pyright-internal-node/dist/packages/pyright-internal/src/parser/parser"; + +import { PyrightLanguageProvider } from "./python/PyrightLanguageProvider"; +import { CodeZoneManager } from "./sas/CodeZoneManager"; import { LanguageServiceProvider, legend } from "./sas/LanguageServiceProvider"; import type { LibCompleteItem } from "./sas/SyntaxDataProvider"; +import { isCustomRegionStartComment } from "./sas/utils"; + +interface DocumentInfo { + document: TextDocument; + changed: boolean; + service?: LanguageServiceProvider; +} -const servicePool: Record = {}; -const documentPool: Record = {}; +export const runServer = ( + connection: Connection, + _pyrightLanguageProvider: PyrightLanguageProvider, +) => { + const documentPool: Record = {}; -let completionProvider: CompletionProvider; -let connection: Connection; -let supportSASGetLibList = false; + let supportSASGetLibList = false; + let registeredAdvancedCapabilities = false; -// Create a simple text document manager. -const documents: TextDocuments = new TextDocuments(TextDocument); + _pyrightLanguageProvider.setSasLspProvider(getLanguageService); -export const init = (conn: Connection): void => { - connection = conn; connection.onInitialize((params) => { if ( params.initializationOptions && @@ -32,6 +67,8 @@ export const init = (conn: Connection): void => { ) { supportSASGetLibList = true; } + _pyrightLanguageProvider.initialize(params, [], []); + const result: InitializeResult = { capabilities: { textDocumentSync: TextDocumentSyncKind.Incremental, @@ -39,96 +76,263 @@ export const init = (conn: Connection): void => { legend, full: true, }, - documentSymbolProvider: true, documentFormattingProvider: true, foldingRangeProvider: true, - hoverProvider: true, - completionProvider: { - triggerCharacters: [" "], - resolveProvider: true, - }, documentOnTypeFormattingProvider: { firstTriggerCharacter: "\n", moreTriggerCharacter: [";"], }, + documentSymbolProvider: { workDoneProgress: true }, + workspaceSymbolProvider: { workDoneProgress: true }, + hoverProvider: { workDoneProgress: true }, + completionProvider: { + triggerCharacters: _pyrightLanguageProvider.getClientCapabilities() + .hasVisualStudioExtensionsCapability + ? [".", "[", "@", '"', "'", " "] + : [".", "[", '"', "'", " "], + resolveProvider: true, + workDoneProgress: true, + completionItem: { + labelDetailsSupport: true, + }, + }, signatureHelpProvider: { - triggerCharacters: ["(", ","], + triggerCharacters: ["(", ",", ")"], + workDoneProgress: true, + }, + workspace: { + workspaceFolders: { + supported: true, + changeNotifications: true, + }, + }, + codeActionProvider: { + codeActionKinds: [ + CodeActionKind.QuickFix, + CodeActionKind.SourceOrganizeImports, + ], }, }, }; return result; }); + connection.onInitialized(() => _pyrightLanguageProvider.onInitialized()); + connection.onRequest(SemanticTokensRequest.type, (params) => { + syncIfDocChange(params.textDocument.uri); const languageService = getLanguageService(params.textDocument.uri); return { data: languageService.getTokens() }; }); - connection.onHover((params) => { - const languageService = getLanguageService(params.textDocument.uri); - - return languageService.completionProvider.getHelp(params.position); + connection.onHover(async (params, token) => { + return await dispatch(params, { + async sas(languageService) { + return await languageService.completionProvider.getHelp( + params.position, + ); + }, + async python(pyrightLanguageService) { + return await pyrightLanguageService.onHover(params, token); + }, + }); }); - connection.onCompletion((params) => { - const languageService = getLanguageService(params.textDocument.uri); - completionProvider = languageService.completionProvider; - - return completionProvider.getCompleteItems(params.position); + connection.onCompletion(async (params, token) => { + return await dispatch(params, { + async sas(languageService) { + if ( + params.context?.triggerCharacter && + params.context.triggerCharacter !== " " + ) { + return undefined; + } + const complitionList = + await languageService.completionProvider.getCompleteItems( + params.position, + ); + if (complitionList) { + for (const item of complitionList.items) { + if (!item.data) { + item.data = {}; + } + item.data._languageService = "sas"; + item.data._uri = params.textDocument.uri; + } + } + return complitionList; + }, + async python(pyrightLanguageService) { + const complitionList = await pyrightLanguageService.onCompletion( + params, + token, + ); + if (complitionList) { + if ( + params.context?.triggerKind === CompletionTriggerKind.Invoked || + params.context?.triggerKind === + CompletionTriggerKind.TriggerForIncompleteCompletions + ) { + if ( + complitionList.items.findIndex( + (item) => item.label === "endsubmit", + ) === -1 + ) { + const endsubmitItem = { + insertText: undefined, + kind: CompletionItemKind.Keyword, + label: "endsubmit", + }; + complitionList.items.push(endsubmitItem); + } + if ( + complitionList.items.findIndex( + (item) => item.label === "endinteractive", + ) === -1 + ) { + const endinteractiveItem = { + insertText: undefined, + kind: CompletionItemKind.Keyword, + label: "endinteractive", + }; + complitionList.items.push(endinteractiveItem); + } + } + for (const item of complitionList.items) { + if (!item.data) { + item.data = {}; + } + item.data._languageService = "python"; + item.data._uri = params.textDocument.uri; + } + } + return complitionList; + }, + }); }); - connection.onCompletionResolve((params) => { - return completionProvider.getCompleteItemHelp(params); + connection.onCompletionResolve(async (completionItem, token) => { + const lang = completionItem.data._languageService; + const label = completionItem.label; + const kind = completionItem.kind; + if ( + lang === "sas" || + (lang === "python" && + kind === CompletionItemKind.Keyword && + ["endsubmit", "endinteractive"].includes(label?.toLowerCase())) + ) { + const languageService = getLanguageService(completionItem.data._uri); + return await languageService.completionProvider.getCompleteItemHelp( + completionItem, + ); + } else if (lang === "python") { + return await _pyrightLanguageProvider.onCompletionResolve( + completionItem, + token, + ); + } else { + return completionItem; + } }); - connection.onDocumentSymbol((params) => { + connection.onDocumentSymbol(async (params, token) => { + syncIfDocChange(params.textDocument.uri); const languageService = getLanguageService(params.textDocument.uri); - return languageService.getDocumentSymbols(); + const sasSymbols = languageService.getDocumentSymbols(); + const pythonSymbols = + (await _pyrightLanguageProvider.onDocumentSymbol(params, token)) ?? []; + let hasPythonCode = false; + const symbolList = [...sasSymbols]; + for (let i = 0; i < symbolList.length; i++) { + const curSymbol = symbolList[i]; + if (isCustomRegionStartComment(curSymbol.name)) { + symbolList.splice(i + 1, 0, ...(curSymbol.children ?? [])); + } else if (curSymbol.name?.toUpperCase() === "PROC PYTHON") { + hasPythonCode = true; + for (const pythonSymbol of pythonSymbols) { + if (!("range" in pythonSymbol)) { + continue; + } + if (isRangeIncluded(curSymbol.range, pythonSymbol.range)) { + curSymbol.children?.push(pythonSymbol); + } + } + } + } + if (registeredAdvancedCapabilities) { + if (!hasPythonCode) { + unregisterAdvancedCapabilities(connection); + } + } else { + if (hasPythonCode) { + registerAdvancedCapabilities(connection); + } + } + return sasSymbols; }); + // todo connection.onFoldingRanges((params) => { + syncIfDocChange(params.textDocument.uri); const languageService = getLanguageService(params.textDocument.uri); return languageService.getFoldingRanges(); }); - connection.onRequest("sas/getFoldingBlock", (params) => { - const languageService = getLanguageService(params.textDocument.uri); - const block = languageService.getFoldingBlock( - params.line, - params.col, - params.strict ?? true, - params.ignoreCustomBlock, - params.ignoreGlobalBlock, - ); - if (!block) { - return undefined; - } else { - return { ...block, outerBlock: undefined, innerBlocks: undefined }; - } + connection.onRequest("sas/getFoldingBlock", async (params) => { + return await dispatch(params, { + async default(languageServices) { + const block = languageServices.sasLanguageService.getFoldingBlock( + params.position.line, + params.position.col, + params.strict ?? true, + params.ignoreCustomBlock, + params.ignoreGlobalBlock, + ); + if (!block) { + return undefined; + } else { + return { ...block, outerBlock: undefined, innerBlocks: undefined }; + } + }, + }); }); - connection.onDocumentOnTypeFormatting((params) => { - const languageService = getLanguageService(params.textDocument.uri); - return languageService.formatOnTypeProvider.getIndentEdit( - params.position.line, - params.position.character, - params.ch, - params.options.tabSize, - params.options.insertSpaces, - ); + connection.onDocumentOnTypeFormatting(async (params) => { + return await dispatch(params, { + async sas(languageService) { + return languageService.formatOnTypeProvider.getIndentEdit( + params.position.line, + params.position.character, + params.ch, + params.options.tabSize, + params.options.insertSpaces, + ); + }, + }); }); - connection.onSignatureHelp((params) => { - const languageService = getLanguageService(params.textDocument.uri); - completionProvider = languageService.completionProvider; - return completionProvider.getSignatureHelp( - params.position, - params.context?.activeSignatureHelp?.activeSignature, - ); + connection.onSignatureHelp(async (params, token) => { + return await dispatch(params, { + async sas(languageService) { + return await languageService.completionProvider.getSignatureHelp( + params.position, + params.context?.activeSignatureHelp?.activeSignature, + ); + }, + async python(pyrightLanguageService) { + // eslint-disable-next-line @typescript-eslint/consistent-type-assertions + return (await pyrightLanguageService.onSignatureHelp( + params, + token, + // eslint-disable-next-line @typescript-eslint/no-explicit-any + )) as any; + }, + }); }); connection.onDocumentFormatting((params) => { + syncIfDocChange(params.textDocument.uri); const languageService = getLanguageService(params.textDocument.uri); return languageService.formatter.format({ tabWidth: params.options.tabSize, @@ -136,37 +340,378 @@ export const init = (conn: Connection): void => { }); }); - documents.onDidChangeContent((event) => { - if (servicePool[event.document.uri]) { - documentPool[event.document.uri] = event.document; - return; - } - servicePool[event.document.uri] = new LanguageServiceProvider( - event.document, + connection.onDidOpenTextDocument(async (params) => { + const doc = TextDocument.create( + params.textDocument.uri, + "sas", + params.textDocument.version, + params.textDocument.text, + ); + documentPool[doc.uri] = { document: doc, changed: false }; + await _pyrightLanguageProvider.onDidOpenTextDocument(params); + }); + + connection.onDidCloseTextDocument(async (params) => { + const uri = params.textDocument.uri; + delete documentPool[uri]; + await _pyrightLanguageProvider.onDidCloseTextDocument(params); + }); + + connection.onDidChangeTextDocument((params) => { + const uri = params.textDocument.uri; + const docInfo = documentPool[uri]; + TextDocument.update( + docInfo.document, + params.contentChanges, + params.textDocument.version, + ); + docInfo.changed = true; + docInfo.service = undefined; + }); + + connection.onDidChangeConfiguration( + async (params: DidChangeConfigurationParams) => { + return await _pyrightLanguageProvider.onDidChangeConfiguration(params); + }, + ); + + connection.onDefinition( + async (params: TextDocumentPositionParams, token: CancellationToken) => { + return await dispatch(params, { + async python(pyrightLanguageService) { + return await pyrightLanguageService.onDefinition(params, token); + }, + }); + }, + ); + + connection.onDeclaration( + async (params: TextDocumentPositionParams, token: CancellationToken) => { + return await dispatch(params, { + async python(pyrightLanguageService) { + return await pyrightLanguageService.onDeclaration(params, token); + }, + }); + }, + ); + + connection.onTypeDefinition( + async (params: TextDocumentPositionParams, token: CancellationToken) => { + return await dispatch(params, { + async python(pyrightLanguageService) { + return await pyrightLanguageService.onTypeDefinition(params, token); + }, + }); + }, + ); + + connection.onReferences( + async ( + params: ReferenceParams, + token: CancellationToken, + workDoneReporter: WorkDoneProgressReporter, + resultReporter: ResultProgressReporter | undefined, + createDocumentRange?: ( + uri: Uri, + result: CollectionResult, + parseResults: ParseFileResults, + ) => DocumentRange, + convertToLocation?: ( + fs: ReadOnlyFileSystem, + ranges: DocumentRange, + ) => Location | undefined, + ) => { + return await dispatch(params, { + async python(pyrightLanguageService) { + return await pyrightLanguageService.onReferences( + params, + token, + workDoneReporter, + resultReporter, + createDocumentRange, + convertToLocation, + ); + }, + }); + }, + ); + + connection.onWorkspaceSymbol( + async ( + params: WorkspaceSymbolParams, + token: CancellationToken, + workDoneProgress, + resultProgress, + ) => { + syncAllChangedDoc(); + return await _pyrightLanguageProvider.onWorkspaceSymbol( + params, + token, + resultProgress, + ); + }, + ); + + connection.onDocumentHighlight( + async (params: DocumentHighlightParams, token: CancellationToken) => { + return await dispatch(params, { + async python(pyrightLanguageService) { + return await pyrightLanguageService.onDocumentHighlight( + params, + token, + ); + }, + }); + }, + ); + + connection.onPrepareRename( + async (params: PrepareRenameParams, token: CancellationToken) => { + return await dispatch(params, { + async python(pyrightLanguageService) { + return await pyrightLanguageService.onPrepareRenameRequest( + params, + token, + ); + }, + }); + }, + ); + + connection.onRenameRequest( + async (params: RenameParams, token: CancellationToken) => { + return await dispatch(params, { + async python(pyrightLanguageService) { + // eslint-disable-next-line @typescript-eslint/consistent-type-assertions + return (await pyrightLanguageService.onRenameRequest( + params, + token, + // eslint-disable-next-line @typescript-eslint/no-explicit-any + )) as any; + }, + }); + }, + ); + + connection.onDidChangeWatchedFiles( + async (params: DidChangeWatchedFilesParams) => { + params.changes.forEach((item) => { + if (item.uri in documentPool) { + syncIfDocChange(item.uri); + } + }); + _pyrightLanguageProvider.onDidChangeWatchedFiles(params); + }, + ); + + connection.onExecuteCommand( + async ( + params: ExecuteCommandParams, + token: CancellationToken, + reporter: WorkDoneProgressReporter, + ) => { + syncAllChangedDoc(); + return await _pyrightLanguageProvider.onExecuteCommand( + params, + token, + reporter, + ); + }, + ); + + const callHierarchy = connection.languages.callHierarchy; + + callHierarchy.onPrepare(async (params, token) => { + return ( + (await dispatch(params, { + async python(pyrightLanguageService) { + return await pyrightLanguageService.onCallHierarchyPrepare( + params, + token, + ); + }, + })) ?? [] + ); + }); + + callHierarchy.onIncomingCalls(async (params, token) => { + return await _pyrightLanguageProvider.onCallHierarchyIncomingCalls( + params, + token, ); }); - // Make the text document manager listen on the connection - // for open, change and close text document events - documents.listen(connection); + callHierarchy.onOutgoingCalls(async (params, token) => { + return await _pyrightLanguageProvider.onCallHierarchyOutgoingCalls( + params, + token, + ); + }); + + connection.onCodeAction(async (params, token) => { + return await dispatch( + { textDocument: params.textDocument, position: params.range.start }, + { + async python(pyrightLanguageService) { + return await pyrightLanguageService.executeCodeAction(params, token); + }, + }, + ); + }); + + connection.onShutdown(async (token) => { + await _pyrightLanguageProvider.onShutdown(token); + }); // Listen on the connection connection.listen(); -}; -function getLanguageService(uri: string) { - if (documentPool[uri]) { + function getLanguageService(uri: string) { + const docInfo = documentPool[uri]; // re-create LanguageServer if document changed - servicePool[uri] = new LanguageServiceProvider(documentPool[uri]); + if (!docInfo.service) { + docInfo.service = new LanguageServiceProvider(docInfo.document); - if (supportSASGetLibList) { - servicePool[uri].setLibService((libId, resolve) => - connection - .sendRequest("sas/getLibList", { libId: libId }) - .then(resolve), - ); + if (supportSASGetLibList) { + docInfo.service.setLibService((libId, resolve) => + connection + .sendRequest("sas/getLibList", { libId: libId }) + .then(resolve), + ); + } } - delete documentPool[uri]; + return docInfo.service!; } - return servicePool[uri]; -} + + const dispatch = async ( + params: { textDocument: { uri: string }; position: Position }, + callbacks: { + sas?: (languageService: LanguageServiceProvider) => Promise; + python?: ( + pyrightLanguageService: PyrightLanguageProvider, + ) => Promise; + default?: (languageServices: { + sasLanguageService: LanguageServiceProvider; + pythonLanguageService?: PyrightLanguageProvider; + }) => Promise; + }, + ) => { + syncIfDocChange(params.textDocument.uri); + const languageService = getLanguageService(params.textDocument.uri); + const codeZoneManager = languageService.getCodeZoneManager(); + const pos = params.position; + 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 ?? [])); + continue; + } + const start = symbol.range.start; + const end = symbol.range.end; + if ( + (start.line < pos.line || + (start.line === pos.line && start.character <= pos.character)) && + (end.line > pos.line || + (end.line === pos.line && end.character >= pos.character)) + ) { + if ( + symbol.name?.toUpperCase() === "PROC PYTHON" && + codeZoneManager.getCurrentZone(pos.line, pos.character) === + CodeZoneManager.ZONE_TYPE.EMBEDDED_LANG + ) { + if (callbacks.python) { + return await callbacks.python(_pyrightLanguageProvider); + } else if (callbacks.default) { + return await callbacks.default({ + sasLanguageService: languageService, + pythonLanguageService: _pyrightLanguageProvider, + }); + } else { + return undefined; + } + } + } + } + if (callbacks.sas) { + return await callbacks.sas(languageService); + } else if (callbacks.default) { + return await callbacks.default({ + sasLanguageService: languageService, + pythonLanguageService: _pyrightLanguageProvider, + }); + } + { + return undefined; + } + }; + + const isRangeIncluded = (a: Range, b: Range) => { + if ( + b.start.line > a.start.line && + (b.end.line < a.end.line || + (b.end.line === a.end.line && b.end.character <= a.end.character)) + ) { + return true; + } + return false; + }; + + const syncIfDocChange = (uri: string) => { + const docInfo = documentPool[uri]; + if (!docInfo.changed) { + return; + } + docInfo.changed = false; + _pyrightLanguageProvider.addContentChange(docInfo.document); + }; + + const syncAllChangedDoc = () => { + for (const uri in documentPool) { + syncIfDocChange(uri); + } + }; + + const advancedCapabilities = [ + "textDocument/declaration", + "textDocument/definition", + "textDocument/typeDefinition", + "textDocument/references", + "textDocument/rename", + "textDocument/documentHighlight", + "textDocument/prepareCallHierarchy", + ]; + + const registerAdvancedCapabilities = async (conn: Connection) => { + if (registeredAdvancedCapabilities) { + return; + } + registeredAdvancedCapabilities = true; + const registerOptions = { + documentSelector: [{ language: "sas" }], + }; + const registrations: Registration[] = []; + for (const capability of advancedCapabilities) { + registrations.push({ + id: capability, + method: capability, + registerOptions, + }); + } + await conn.sendRequest(RegistrationRequest.type, { registrations }); + }; + + const unregisterAdvancedCapabilities = async (conn: Connection) => { + if (!registeredAdvancedCapabilities) { + return; + } + registeredAdvancedCapabilities = false; + const unregisterations: Unregistration[] = []; + for (const capability of advancedCapabilities) { + unregisterations.push({ id: capability, method: capability }); + } + await conn.sendRequest(UnregistrationRequest.type, { + unregisterations: unregisterations, + }); + }; +}; diff --git a/syntaxes/sas.tmLanguage.json b/syntaxes/sas.tmLanguage.json index b18039af3..834732e1b 100644 --- a/syntaxes/sas.tmLanguage.json +++ b/syntaxes/sas.tmLanguage.json @@ -35,7 +35,7 @@ "include": "#strings-or-comments" }, { - "begin": "(?i)(?<=submit|interactive)(\\s|/\\*.*?\\*/)*;", + "begin": "(?i)(?<=submit|interactive|i)(\\s|/\\*.*?\\*/)*;", "end": "(?i)(endsubmit|endinteractive)(\\s|/\\*.*?\\*/)*;", "name": "source.lua", "beginCaptures": { @@ -60,7 +60,7 @@ "include": "#strings-or-comments" }, { - "begin": "(?i)(?<=submit|interactive)(\\s|/\\*.*?\\*/)*;", + "begin": "(?i)(?<=submit|interactive|i)(\\s|/\\*.*?\\*/)*;", "end": "(?i)(endsubmit|endinteractive)(\\s|/\\*.*?\\*/)*;", "name": "source.python", "beginCaptures": { diff --git a/tools/build.mjs b/tools/build.mjs index ae9b34f0b..f9e96f414 100644 --- a/tools/build.mjs +++ b/tools/build.mjs @@ -1,5 +1,7 @@ import concurrently from "concurrently"; import esbuild from "esbuild"; +import fs from "fs"; +import path from "path"; console.log("start"); const dev = process.argv[2]; @@ -79,7 +81,26 @@ if (process.env.npm_config_webviews || process.env.npm_config_client) { ]); await result.then( - () => {}, + () => { + const foldersToCopy = [ + { + src: "./server/node_modules/jsonc-parser/lib/umd/impl", + dest: "./server/dist/node/impl", + }, + { + src: "./server/node_modules/pyright-internal-node/dist/packages/pyright-internal/typeshed-fallback", + dest: "./server/dist/node/typeshed-fallback", + }, + { + src: "./server/src/python/sas", + dest: "./server/dist/node/typeshed-fallback/stubs/sas", + }, + ]; + for (const item of foldersToCopy) { + fs.cpSync(item.src, item.dest, { recursive: true }); + console.log(`${item.src} was copied to ${item.dest}`); + } + }, () => console.error("Assets failed to build successfully"), ); } diff --git a/webpack.config.js b/webpack.config.js index c5a5f505e..4d8e995d7 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -8,6 +8,7 @@ /** @typedef {import('webpack').Configuration} WebpackConfig **/ const path = require("path"); +const { ProvidePlugin, DefinePlugin } = require("webpack"); /** @type WebpackConfig */ const browserClientConfig = { @@ -71,8 +72,22 @@ const browserServerConfig = { extensions: [".ts", ".js"], // support ts-files and js-files alias: { "../node": path.resolve(__dirname, "server/src/browser"), + "./common/realFileSystem": path.resolve( + __dirname, + "server/src/python/browser/fakeFileSystem", + ), + }, + fallback: { + path: require.resolve("path-browserify"), + os: false, + crypto: false, + // buffer: require.resolve("buffer/"), + stream: false, + child_process: false, + fs: false, + assert: false, + util: false, }, - fallback: {}, }, module: { rules: [ @@ -85,6 +100,17 @@ const browserServerConfig = { }, ], }, + { + test: /python[\\|/]browser[\\|/]typeShed\.ts$/, + use: [ + { + loader: path.resolve( + __dirname, + "server/src/python/browser/typeshed-loader", + ), + }, + ], + }, { test: /\.properties$/, exclude: /node_modules/, @@ -94,11 +120,20 @@ const browserServerConfig = { }, externals: { vscode: "commonjs vscode", // ignored because it doesn't exist + fsevents: "commonjs2 fsevents", }, performance: { hints: false, }, devtool: "source-map", + plugins: [ + new DefinePlugin({ + process: "{ env: {}, execArgv: [], cwd: () => '/' }", + }), + new ProvidePlugin({ + Buffer: ["buffer", "Buffer"], + }), + ], }; module.exports = [browserClientConfig, browserServerConfig]; diff --git a/website/docs/installation.md b/website/docs/installation.md index 15c23565e..3d6fa62a2 100644 --- a/website/docs/installation.md +++ b/website/docs/installation.md @@ -4,7 +4,7 @@ sidebar_position: 3 # Installation -Install the latest version of Visual Studio Code (version 1.82 or later). +Install the latest version of Visual Studio Code (version 1.89 or later). To install the SAS extension: diff --git a/website/package-lock.json b/website/package-lock.json index 655f9c746..47e662c0c 100644 --- a/website/package-lock.json +++ b/website/package-lock.json @@ -10,7 +10,7 @@ "dependencies": { "@docusaurus/preset-classic": "3.6.3", "@docusaurus/theme-mermaid": "3.6.3", - "@easyops-cn/docusaurus-search-local": "0.45.0", + "@easyops-cn/docusaurus-search-local": "0.46.1", "prism-react-renderer": "^2.4.0", "react": "^18.3.1", "react-dom": "^18.3.1" @@ -18,7 +18,7 @@ "devDependencies": { "@docusaurus/module-type-aliases": "3.6.3", "@docusaurus/tsconfig": "3.6.3", - "typescript": "~5.6.3" + "typescript": "~5.7.2" }, "engines": { "node": ">=18.0" @@ -3855,9 +3855,10 @@ } }, "node_modules/@easyops-cn/docusaurus-search-local": { - "version": "0.45.0", - "resolved": "https://registry.npmjs.org/@easyops-cn/docusaurus-search-local/-/docusaurus-search-local-0.45.0.tgz", - "integrity": "sha512-ccJjeYmBHrv2v8Y9eQnH79S0PEKcogACKkEatEKPcad7usQj/14jA9POUUUYW/yougLSXghwe+uIncbuUBuBFg==", + "version": "0.46.1", + "resolved": "https://registry.npmjs.org/@easyops-cn/docusaurus-search-local/-/docusaurus-search-local-0.46.1.tgz", + "integrity": "sha512-kgenn5+pctVlJg8s1FOAm9KuZLRZvkBTMMGJvTTcvNTmnFIHVVYzYfA2Eg+yVefzsC8/cSZGKKJ0kLf8I+mQyw==", + "license": "MIT", "dependencies": { "@docusaurus/plugin-content-docs": "^2 || ^3", "@docusaurus/theme-translations": "^2 || ^3", @@ -3868,6 +3869,7 @@ "@node-rs/jieba": "^1.6.0", "cheerio": "^1.0.0", "clsx": "^1.1.1", + "comlink": "^4.4.2", "debug": "^4.2.0", "fs-extra": "^10.0.0", "klaw-sync": "^6.0.0", @@ -6277,6 +6279,12 @@ "node": ">=10" } }, + "node_modules/comlink": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/comlink/-/comlink-4.4.2.tgz", + "integrity": "sha512-OxGdvBmJuNKSCMO4NTl1L47VRp6xn2wG4F/2hYzB6tiCb709otOxtEYCSvK80PtjODfXXZu8ds+Nw5kVCjqd2g==", + "license": "Apache-2.0" + }, "node_modules/comma-separated-tokens": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz", @@ -17217,9 +17225,10 @@ } }, "node_modules/typescript": { - "version": "5.6.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.6.3.tgz", - "integrity": "sha512-hjcS1mhfuyi4WW8IWtjP7brDrG2cuDZukyrYrSauoXGNgx0S7zceP07adYkJycEr56BOUTNPzbInooiN3fn1qw==", + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.7.2.tgz", + "integrity": "sha512-i5t66RHxDvVN40HfDd1PsEThGNnlMCMT3jMUuoh9/0TaqWevNontacunWyN02LA9/fIbEWlcHZcgTKb9QoaLfg==", + "license": "Apache-2.0", "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" diff --git a/website/package.json b/website/package.json index 515fef7f2..d0c4dd9de 100644 --- a/website/package.json +++ b/website/package.json @@ -17,7 +17,7 @@ "dependencies": { "@docusaurus/preset-classic": "3.6.3", "@docusaurus/theme-mermaid": "3.6.3", - "@easyops-cn/docusaurus-search-local": "0.45.0", + "@easyops-cn/docusaurus-search-local": "0.46.1", "prism-react-renderer": "^2.4.0", "react": "^18.3.1", "react-dom": "^18.3.1" @@ -25,7 +25,7 @@ "devDependencies": { "@docusaurus/module-type-aliases": "3.6.3", "@docusaurus/tsconfig": "3.6.3", - "typescript": "~5.6.3" + "typescript": "~5.7.2" }, "browserslist": { "production": [