diff --git a/.eslintignore b/.eslintignore new file mode 100644 index 000000000..b744996d7 --- /dev/null +++ b/.eslintignore @@ -0,0 +1 @@ +scripts diff --git a/.eslintrc.yml b/.eslintrc.yml new file mode 100644 index 000000000..e0f9ec2c5 --- /dev/null +++ b/.eslintrc.yml @@ -0,0 +1,37 @@ +env: + node: true +extends: + - 'eslint:recommended' + - 'plugin:@typescript-eslint/recommended' + - 'plugin:@typescript-eslint/recommended-requiring-type-checking' +parser: '@typescript-eslint/parser' +parserOptions: + ecmaVersion: 12 + sourceType: module + project: + - ./tsconfig.json + - ./html/httpgd/tsconfig.json + - ./html/help/tsconfig.json +plugins: + - '@typescript-eslint' + - 'jsdoc' +rules: + semi: 'error' + no-extra-semi: 'warn' + curly: 'warn' + quotes: + - 'error' + - 'single' + - allowTemplateLiterals: true + eqeqeq: 'error' + no-empty: + - 'warn' + - allowEmptyCatch: true + '@typescript-eslint/no-inferrable-types': + - 'warn' + - ignoreParameters: true + ignoreProperties: true + indent: + - 'error' + - 4 + - SwitchCase: 1 diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 000000000..dfe077042 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,2 @@ +# Auto detect text files and perform LF normalization +* text=auto diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index 7fb2d52f1..486f414ac 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -2,32 +2,38 @@ name: Bug report about: Create a report to help us improve title: '' -labels: '' +labels: bug assignees: '' - --- + + **Describe the bug** A clear and concise description of what the bug is. **To Reproduce** Steps to reproduce the behavior: + 1. Go to '...' 2. Click on '....' 3. Scroll down to '....' 4. See error -**Do you want to fix by self? (I hope your help!)** +**Can you fix this issue by yourself? (We appreciate the help)** Yes / No -**(If yes,) what kind of help do you want? (e.g. Which file should I fix, Survey (related documents)** +**(If yes,) can we assist you with anything?** -(If related)setting.json +**(If applicable) Please attach `setting.json`** -```json +```jsonc // R.exe path for windows -"r.rterm.windows": "C:\\Program Files\\R\\R-3.4.4\\bin\\x64\\R.exe", +"r.rterm.windows": "", // R path for Mac OS X "r.rterm.mac": "/usr/local/bin/R", @@ -49,6 +55,12 @@ Yes / No // Use bracketed paste mode "r.bracketedPaste": false, + +// Enable R session watcher +"r.sessionWatcher": true, + +// Delay in milliseconds before sending each line to rterm (only applies if r.bracketedPaste is false) +"r.rtermSendDelay": 8, ``` **Expected behavior** @@ -56,11 +68,14 @@ A clear and concise description of what you expected to happen. **Screenshots** If applicable, add screenshots to help explain your problem. +You can show the keyboard contents by pressing `F1` and `Developer: toggle screencast mode` **Environment (please complete the following information):** - - OS: [e.g. iOS] - - VSCode Version [e.g. 22] - - R Version + +- OS: [e.g. Windows, macOS, Linux] +- VSCode Version: [e.g. 1.42.0] +- R Version: [e.g. 3.6.2] +- vscode-R version: [e.g. 1.2.2] **Additional context** Add any other context about the problem here. diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md index bbcbbe7d6..982a4dc0d 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.md +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -2,7 +2,7 @@ name: Feature request about: Suggest an idea for this project title: '' -labels: '' +labels: feature-request assignees: '' --- diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml new file mode 100644 index 000000000..fa7dda10c --- /dev/null +++ b/.github/workflows/main.yml @@ -0,0 +1,64 @@ +name: main +on: [push, pull_request] +env: + SCRIPT_DIR: ./.github/scripts + +jobs: + test: + strategy: + matrix: + os: [macos-latest, ubuntu-latest, windows-latest] + runs-on: ${{ matrix.os }} + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-node@v3 + with: + node-version: 18 + - run: npm install + - name: Run tests + uses: GabrielBB/xvfb-action@v1.0 + with: + run: npm run test + build: + runs-on: ubuntu-latest + env: + VSIX_FILE: vscode-R.vsix + steps: + - uses: actions/checkout@v3 + - run: npm install + - uses: lannonbr/vsce-action@4.0.0 + with: + args: "package -o $VSIX_FILE" + - uses: actions/upload-artifact@v4 + with: + name: ${{ env.VSIX_FILE }} + path: ${{ env.VSIX_FILE }} + eslint: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-node@v3 + with: + node-version: 18 + - run: npm install + - run: npm run lint + lint: + runs-on: ubuntu-latest + env: + GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} + steps: + - uses: actions/checkout@v4 + + - uses: r-lib/actions/setup-r@v2 + with: + use-public-rspm: true + + - name: Install lintr + run: install.packages("lintr") + shell: Rscript {0} + + - name: Lint root directory + run: lintr::lint_dir("./R") + shell: Rscript {0} + env: + LINTR_ERROR_ON_LINT: true diff --git a/.github/workflows/pre-release.yml b/.github/workflows/pre-release.yml new file mode 100644 index 000000000..9e02c3233 --- /dev/null +++ b/.github/workflows/pre-release.yml @@ -0,0 +1,54 @@ +name: pre-release + +# creates/updates a pre-release with the .vsix + +on: + push: + branches: ["master"] + +env: + FILE_OUT: r-latest.vsix + SCRIPT_DIR: ./.github/scripts + +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - run: npm install + - uses: lannonbr/vsce-action@4.0.0 + with: + args: "package -o $FILE_OUT" + - uses: actions/upload-artifact@v4 + with: + name: "${{ env.FILE_OUT }}" + path: "${{ env.FILE_OUT }}" + + pre-release: + name: Pre-Release + needs: build + runs-on: ubuntu-latest + + steps: + - name: Update tag + uses: richardsimko/update-tag@v1 + with: + tag_name: latest + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + - name: Download artifacts + uses: actions/download-artifact@v4.1.7 + with: + path: "artifacts/" + - name: Upload artifacts + uses: meeDamian/github-release@2.0 + with: + token: ${{ secrets.GITHUB_TOKEN }} + tag: latest + commitish: master + name: Development Build + body: Contains the vsix-file from the latest push to master. + prerelease: true + files: "artifacts/*/*" + gzip: false + allow_override: true diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 000000000..9ff5de961 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,69 @@ +name: release + +# runs when a tag v* is pushed +# releases the extension to GitHub and the vscode marketplace + +on: + push: + tags: ["v*"] + +env: + SCRIPT_DIR: ./.github/scripts + +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - run: npm install + - uses: lannonbr/vsce-action@4.0.0 + with: + args: "package" + - name: Identify output file # can be retrieved as steps.filenames.outputs.file_out + id: filenames + run: echo "::set-output name=file_out::$(ls | grep "^.*\.vsix$" | head -1)" + - uses: actions/upload-artifact@v4 + with: + name: ${{ steps.filenames.outputs.file_out }} + path: ${{ steps.filenames.outputs.file_out }} + + release: + name: Release + needs: build + runs-on: ubuntu-latest + + steps: + - name: Download artifacts + uses: actions/download-artifact@v4.1.7 + with: + path: "artifacts/" + - name: Get version from tag + id: get_version + run: echo ::set-output name=version::${GITHUB_REF/refs\/tags\/v/} + - name: Create release + uses: marvinpinto/action-automatic-releases@latest + with: + repo_token: ${{ secrets.GITHUB_TOKEN }} + files: "artifacts/*/*" + prerelease: false + draft: false + + + publish: + name: Publish + timeout-minutes: 30 + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - run: npm install + - name: Publish to Open VSX Registry + uses: HaaLeo/publish-vscode-extension@v1 + id: publishToOpenVSX + with: + pat: ${{ secrets.OPEN_VSX_TOKEN }} + - name: Publish to Visual Studio Marketplace + uses: HaaLeo/publish-vscode-extension@v1 + with: + pat: ${{ secrets.VSCE_TOKEN }} + registryUrl: https://marketplace.visualstudio.com + extensionFile: ${{ steps.publishToOpenVSX.outputs.vsixPath }} diff --git a/.gitignore b/.gitignore index 4fd3b3eca..0841696a1 100644 --- a/.gitignore +++ b/.gitignore @@ -4,4 +4,15 @@ node_modules *.xlsx *.xls *.DS_Store -dist \ No newline at end of file +*.vsix +dist +.vscode-test + +html/**/*.js + +*.temp +*.tmp +tmp.* +temp.* +tmp +temp diff --git a/.markdownlint.json b/.markdownlint.json new file mode 100644 index 000000000..88fa2aa9f --- /dev/null +++ b/.markdownlint.json @@ -0,0 +1,7 @@ +{ + "no-inline-html": { + "allowed_elements": [ "details", "summary", "br" ] + }, + "line-length": false, + "no-trailing-punctuation": false +} \ No newline at end of file diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 000000000..0db3279e4 --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,3 @@ +{ + +} diff --git a/.vscode/extentions.json b/.vscode/extentions.json deleted file mode 100644 index 1f5f26ab3..000000000 --- a/.vscode/extentions.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "recommendations": [ - "eg2.tslint", - "GrapeCity.gc-excelviewer" - ] -} \ No newline at end of file diff --git a/.vscode/launch.json b/.vscode/launch.json index cd6b87bd0..977a98811 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -1,28 +1,47 @@ -// A launch configuration that compiles the extension and then opens it inside a new window { - "version": "0.1.0", - "configurations": [ - { - "name": "Launch Extension", - "type": "extensionHost", - "request": "launch", - "runtimeExecutable": "${execPath}", - "args": ["--extensionDevelopmentPath=${workspaceRoot}" ], - "stopOnEntry": false, - "sourceMaps": true, - "outFiles": [ "${workspaceRoot}/out/src/**/*.js" ], - "preLaunchTask": "npm" - }, - { - "name": "Launch Tests", - "type": "extensionHost", - "request": "launch", - "runtimeExecutable": "${execPath}", - "args": ["--extensionDevelopmentPath=${workspaceRoot}", "--extensionTestsPath=${workspaceRoot}/out/test" ], - "stopOnEntry": false, - "sourceMaps": true, - "outFiles": [ "${workspaceRoot}/out/test/**/*.js" ], - "preLaunchTask": "npm" - } - ] + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "name": "Launch Extension", + "type": "extensionHost", + "request": "launch", + "runtimeExecutable": "${execPath}", + "args": [ + "--extensionDevelopmentPath=${workspaceFolder}" + ], + "outFiles": [ + "${workspaceFolder}/out/src/**/*.js" + ], + }, + { + "name": "Launch Extension (--disable-extensions)", + "type": "extensionHost", + "request": "launch", + "runtimeExecutable": "${execPath}", + "args": [ + "--extensionDevelopmentPath=${workspaceFolder}", + "--disable-extensions" + ], + "outFiles": [ + "${workspaceFolder}/out/src/**/*.js" + ], + }, + { + "name": "Extension Tests", + "type": "extensionHost", + "request": "launch", + "runtimeExecutable": "${execPath}", + "args": [ + "--extensionDevelopmentPath=${workspaceFolder}", + "--extensionTestsPath=${workspaceFolder}/out/test/suite/index" + ], + "outFiles": [ + "${workspaceFolder}/out/test/**/*.js" + ], + "preLaunchTask": "npm: pretest" + } + ] } diff --git a/.vscode/settings.json b/.vscode/settings.json index 7877e3fc6..d39fbe699 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -6,5 +6,9 @@ "search.exclude": { "out": true // set this to false to include "out" folder in search results }, - "typescript.tsdk": "./node_modules/typescript/lib" // we want to use the TS server from our node_modules folder to control its version + "typescript.tsdk": "./node_modules/typescript/lib", // we want to use the TS server from our node_modules folder to control its version + "r.lsp.diagnostics": true, + "editor.codeActionsOnSave": { + "source.fixAll.markdownlint": "explicit" + } } \ No newline at end of file diff --git a/.vscode/tasks.json b/.vscode/tasks.json index 1e37eb7bb..5abf195ae 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -1,30 +1,83 @@ -// Available variables which can be used inside of strings. -// ${workspaceRoot}: the root folder of the team -// ${file}: the current opened file -// ${fileBasename}: the current opened file's basename -// ${fileDirname}: the current opened file's dirname -// ${fileExtname}: the current opened file's extension -// ${cwd}: the current working directory of the spawned process - -// A task runner that calls a custom npm script that compiles the extension. { - "version": "0.1.0", - - // we want to run npm - "command": "npm", - - // the command is a shell script - "isShellCommand": true, - - // show the output window only if unrecognized errors occur. - "showOutput": "silent", - - // we run the custom script "compile" as defined in package.json - "args": ["run", "compile", "--loglevel", "silent"], - - // The tsc compiler is started in watching mode - "isBackground": true, - - // use the standard tsc in watch mode problem matcher to find compile problems in the output. - "problemMatcher": "$tsc-watch" -} \ No newline at end of file + // See https://go.microsoft.com/fwlink/?LinkId=733558 + // for the documentation about the tasks.json format + "version": "2.0.0", + "tasks": [ + { + "label": "Package Extension", + "type": "shell", + "problemMatcher": [], + "command": "vsce", + "args": [ + "package", + "-o", + "${workspaceFolderBasename}.vsix" + ] + }, + { + "label": "Install Extension", + "type": "shell", + "problemMatcher": [], + "command": "code", + "args": [ + "--install-extension", + "${workspaceFolderBasename}.vsix", + "--force" + ], + "dependsOn": ["Package Extension"], + "group": { + "kind": "build", + "isDefault": true + } + }, + { + "type": "npm", + "script": "build", + "problemMatcher": "$tsc" + }, + { + "type": "npm", + "script": "watch", + "group": "build", + // install https://marketplace.visualstudio.com/items?itemName=eamodio.tsl-problem-matcher + "problemMatcher": ["$ts-webpack-watch"], + "isBackground": true + }, + { + "type": "npm", + "script": "watchHelp", + "group": "build", + "problemMatcher": "$tsc-watch", + "isBackground": true + }, + { + "type": "npm", + "script": "watchHttpgd", + "group": "build", + "problemMatcher": "$tsc-watch", + "isBackground": true + }, + { + "label": "watchAll", + "dependsOn": [ + "npm: watch", + "npm: watchHelp", + "npm: watchHttpgd" + ], + "problemMatcher": [] + }, + { + "type": "npm", + "script": "pretest", + "problemMatcher": "$tsc-watch", + "isBackground": true, + "presentation": { + "reveal": "never" + }, + "group": { + "kind": "test", + "isDefault": true + }, + } + ] +} diff --git a/.vscodeignore b/.vscodeignore index 9317edfdf..471c3f970 100644 --- a/.vscodeignore +++ b/.vscodeignore @@ -1,18 +1,21 @@ -.vscode/** +.csv +.github/** +.gitignore +.vscode .vscode-test/** +.vscode/** +.xls +.xlsx +**/*.map +html/**/*.ts out/test/** -test/** +src/ src/** -**/*.map -.gitignore -tsconfig.json +test/** +**/tsconfig.json vsc-extension-quickstart.md -.csv -.xlsx -.xls -.vscode -node_modules -out/ -src/ -tsconfig.json -webpack.config.json \ No newline at end of file +esbuild.js +.markdownlint.json +.eslintignore +node_modules +out/ diff --git a/CHANGELOG.md b/CHANGELOG.md index 777fa9619..1466f8524 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,312 +1,1822 @@ -# Change Log +# Changelog -## 1.1.5 +## 2.8.8 - 2026-03-24 -* Replace deprecated function (Refactoring) -* Add alwaysUseActiveTerminal setting (fix #123) +### Features -## 1.1.4 +* feat: change default of r.lsp.multiServer to false -* Fixed spelling, improved formatting #129 (Thank you @wleoncio) -* Automatically comment new lines in roxygen sections (fix #124) -* Fix send code for newlines on Windows (fix #114) -* Add auto-completion of roxygen tags (fix #128) -* Change cursorMove to wrappedLineFirstNonWhitespaceCharacter (fix 126) +**Full Changelog**: -## v1.1.3 +## 2.8.7 - 2026-03-15 -* RMarkdown knit support (fix #121) (Thank you @dominicwhite) +### Bug Fixes -## v1.1.2 +* fix: correct r.term and r.path setting names in error message -* Fix send code for newlines and Radian #114 #117 +### Features -## v1.1.1 +* feat: support multi-root workspaces in single-server mode -* Fix Preview Environment for variable x (fix #111) by @andycraig -* Fix Preview Environment for multi-class objects (fix #111) by @andycraig -* Fix danger package dependency +### Other -## v1.1.0 +* Allow bracketedPaste on win32 platform ([#1631](https://github.com/REditorSupport/vscode-R/issues/1631)) +* feat: default to single language server for multi-root workspaces ([#1682](https://github.com/REditorSupport/vscode-R/issues/1682)) -* Fix for R markdown config -* Fix for valunerability +**Full Changelog**: -## v1.0.9 +## 2.8.6 - 2025-05-31 -* Fix check for Excel Viewer extension +### Other -## v1.0.7 +* Syntax update and bump to 2.8.6 ([#1605](https://github.com/REditorSupport/vscode-R/issues/1605)) +* Show sidebar icon only when extension is active ([#1579](https://github.com/REditorSupport/vscode-R/issues/1579)) +* Move R and R markdown syntaxes to vscode-R-syntax ([#1606](https://github.com/REditorSupport/vscode-R/issues/1606)) -* Add web pack for performance by @andycraig +### Refactor -## v1.0.6 +* refactor: restructure files ([#1613](https://github.com/REditorSupport/vscode-R/issues/1613)) -* Add runSelectionInActiveTerm command #104 (fix #80 #102) (Thank you @andycraig) +**Full Changelog**: -## v1.0.4 +## 2.8.5 - 2025-04-10 -* Shortcuts with R functions #101 -(fix #100) (Thank you @MaTo04) +### Other +* Don't close stale issues ([#1522](https://github.com/REditorSupport/vscode-R/issues/1522)) +* Do not mark issues as stale +* Do not mark issues as stale ([#1561](https://github.com/REditorSupport/vscode-R/issues/1561)) +* Rsyntax ([#1560](https://github.com/REditorSupport/vscode-R/issues/1560)) +* Bump path-to-regexp from 6.2.2 to 6.3.0 ([#1563](https://github.com/REditorSupport/vscode-R/issues/1563)) +* chore ([#1598](https://github.com/REditorSupport/vscode-R/issues/1598)) +* Release 2.8.5 ([#1599](https://github.com/REditorSupport/vscode-R/issues/1599)) -## v1.0.3 +**Full Changelog**: -* Fix Preview Dataframe command #67(fix #97) (Thank you @andycraig) +## 2.8.4 - 2024-05-18 -## v1.0.2 +### Bug Fixes -* Remove excel dependency +* Fix code -## v1.0.1 +### Other -* Fix Dependency -* Refactoring +* Upgrade dependencies +* Upgrade dependencies +* Update package.json +* release 2.8.4 -## v1.0.0 +**Full Changelog**: -* Sorry, supporting this extension is ended. Please looking forward to coming new one (). +## 2.8.3 - 2024-05-07 -## v0.6.2 +### Bug Fixes -* fix wordPattern to avoid `.` -* fix run selection +* Fix multiline smart-knit ([#1493](https://github.com/REditorSupport/vscode-R/issues/1493)) +* Fix RMD Progress Bar ([#1491](https://github.com/REditorSupport/vscode-R/issues/1491)) -## v0.6.1 +### Other -* Added detection of bracket and pipe blocks #82 (fix #26) (Thank you @andycraig) -* Fix dependency +* Substitute variables in `r.rpath` and `r.rterm` settings ([#1444](https://github.com/REditorSupport/vscode-R/issues/1444)) +* Substitute variables in r.rterm.option and r.lsp.args settings +* getRLibPaths uses substituteVariables +* Improve code chunk handling in base `.R` files ([#1454](https://github.com/REditorSupport/vscode-R/issues/1454)) +* Improvements to `.R` file chunks: ([#1455](https://github.com/REditorSupport/vscode-R/issues/1455)) +* remove '.' as an R language editor.wordSeparators ([#1503](https://github.com/REditorSupport/vscode-R/issues/1503)) +* `numeric_version()` wants character as of R 4.4 ([#1520](https://github.com/REditorSupport/vscode-R/issues/1520)) +* handling terminals created by vscode-Python ([#1511](https://github.com/REditorSupport/vscode-R/issues/1511)) +* `numeric_version()` with character arg ([#1523](https://github.com/REditorSupport/vscode-R/issues/1523)) +* Bump ejs from 3.1.7 to 3.1.10 ([#1519](https://github.com/REditorSupport/vscode-R/issues/1519)) +* Update init.R ([#1525](https://github.com/REditorSupport/vscode-R/issues/1525)) +* release 2.8.3 -## v0.6.0 +**Full Changelog**: -* Remove lintr function. If you want to use lintr, please install R LSP Client +## 2.8.2 - 2023-10-08 -## v0.5.9 +### Other -* Fix for security dependencies +* Bump semver from 7.3.5 to 7.5.3 ([#1388](https://github.com/REditorSupport/vscode-R/issues/1388)) +* Bump word-wrap from 1.2.3 to 1.2.4 ([#1395](https://github.com/REditorSupport/vscode-R/issues/1395)) +* Update built-in function match regex ([#1431](https://github.com/REditorSupport/vscode-R/issues/1431)) +* Add Option to Sync renv Cache ([#1423](https://github.com/REditorSupport/vscode-R/issues/1423)) +* Add task to run `testthat::test_file()` on current file ([#1415](https://github.com/REditorSupport/vscode-R/issues/1415)) +* Fix indentation_linter message ([#1433](https://github.com/REditorSupport/vscode-R/issues/1433)) +* allow to invoke R terminal also in relative paths ([#1398](https://github.com/REditorSupport/vscode-R/issues/1398)) +* Upgrade ag-grid-community to v30.2.0 ([#1434](https://github.com/REditorSupport/vscode-R/issues/1434)) +* Upgrade vscode-languageclient to 9.0.1 ([#1435](https://github.com/REditorSupport/vscode-R/issues/1435)) +* release 2.8.2 -## v0.5.8 +**Full Changelog**: -* Fix Run Selected has strange behavior #42 (Thank you @Ladvien) +## 2.8.1 - 2023-06-09 -## v0.5.7 +### Bug Fixes -* Disabled lintr for default setting that is already implemented by LSP -* Fix Commented lines are not ignored when determining code blocks #61 (Thank you @Ladvien) +* Fix handling pageSize=0 ([#1364](https://github.com/REditorSupport/vscode-R/issues/1364)) +* Fix help panel in remote host ([#1374](https://github.com/REditorSupport/vscode-R/issues/1374)) +* Fix install package name ([#1377](https://github.com/REditorSupport/vscode-R/issues/1377)) -## v0.5.6 +### Other -* Fix some dependencies for perform and developments +* Add `r.lsp.multiServer` setting ([#1375](https://github.com/REditorSupport/vscode-R/issues/1375)) +* Upgrade to ag-grid-community-v30.0.0 ([#1379](https://github.com/REditorSupport/vscode-R/issues/1379)) +* release 2.8.1 -## v0.5.5 +**Full Changelog**: -* Add package dev commands #58 (Thank you @jacob-long) +## 2.8.0 - 2023-04-28 -## v0.5.4 +### Bug Fixes -* fix snippets -* R term name to R interactive (fix #46) -* Send code from Rmd chunk to terminal (fix #49) -* Depend R language server extension +* Fix markdown lint -## v0.5.3 +### Other -* fix default r.rterm.option again to `["--no-save", "--no-restore", "--no-site-file"]` +* Hide most help panel commands in command palette ([#1327](https://github.com/REditorSupport/vscode-R/issues/1327)) +* Hide liveshare toggle command ([#1330](https://github.com/REditorSupport/vscode-R/issues/1330)) +* Bump webpack from 5.38.1 to 5.76.0 ([#1331](https://github.com/REditorSupport/vscode-R/issues/1331)) +* Handle errors in `getAliases.R` ([#1334](https://github.com/REditorSupport/vscode-R/issues/1334)) +* Remove unused ag-theme-balham-dark.min.css +* Upgrade to ag-grid-community-v29.3.0 ([#1346](https://github.com/REditorSupport/vscode-R/issues/1346)) +* Websocket communication ([#1151](https://github.com/REditorSupport/vscode-R/issues/1151)) +* RMD Preview: font-size setting ([#1333](https://github.com/REditorSupport/vscode-R/issues/1333)) +* release 2.8.0 +* release 2.8.0 -## v0.5.2 +**Full Changelog**: -* fix default r.rterm.option to `["--no-save", "--vanilla"]` +## 2.7.2 - 2023-03-06 -## v0.5.1 +### Other -* Support code region by `#region` and `#endregion` +* Basic unit test for workspace viewer ([#1305](https://github.com/REditorSupport/vscode-R/issues/1305)) +* Update yarn.lock +* Upgrade vscode-languageclient to 8.1.0 ([#1315](https://github.com/REditorSupport/vscode-R/issues/1315)) +* Explicit workspace behaviour ([#1317](https://github.com/REditorSupport/vscode-R/issues/1317)) +* Ensure workspace is cleared ([#1318](https://github.com/REditorSupport/vscode-R/issues/1318)) +* Added r.view command: View(variable) ([#1319](https://github.com/REditorSupport/vscode-R/issues/1319)) +* Always check pid before clearing workspace ([#1321](https://github.com/REditorSupport/vscode-R/issues/1321)) +* Workspace viewer command visibility ([#1323](https://github.com/REditorSupport/vscode-R/issues/1323)) +* release 2.7.2 -## v0.5.0 +**Full Changelog**: -* Support package lint +## 2.7.1 - 2023-02-15 -## v0.4.9 +### Bug Fixes -* Add shebang support for R syntax highlight #33(Thank you @dongzhuoer) -* Added block detection and execute whole block #32(Thank you @Ladvien) -* Proposed fix for Load Chunk problems #27 #31(Thank you @Ladvien) -* Update some snippets from VS +* Fix broken tests ([#1302](https://github.com/REditorSupport/vscode-R/issues/1302)) -## v0.4.8 +### Other -* Fix Windows key map -* Add some snippets from VS +* Bump minimatch from 3.0.4 to 3.1.2 ([#1271](https://github.com/REditorSupport/vscode-R/issues/1271)) +* Make help preview async ([#1273](https://github.com/REditorSupport/vscode-R/issues/1273)) +* Add the column name to the tooltip on data viewer ([#1278](https://github.com/REditorSupport/vscode-R/issues/1278)) +* Ability to set echo=TRUE in Run source by default ([#1286](https://github.com/REditorSupport/vscode-R/issues/1286)) +* Remove leading comments from terminal submission (#1244) ([#1245](https://github.com/REditorSupport/vscode-R/issues/1245)) +* Upgrade ag-grid-community to v29.0.0 ([#1290](https://github.com/REditorSupport/vscode-R/issues/1290)) +* Migrate to @vscode/test-electron ([#1303](https://github.com/REditorSupport/vscode-R/issues/1303)) +* release 2.7.1 -## v0.4.7 +**Full Changelog**: -* Fix syntax -* Fix Readme -* Fix icon +## 2.7.0 - 2022-12-04 -## v0.4.6 +### Bug Fixes -* Added Environment Viewer command +* fix typo in dialog ([#1249](https://github.com/REditorSupport/vscode-R/issues/1249)) -## v0.4.5 +### Other -* Fix syntax little -* Set icon dark and light -* Improve data viewer perform(Thank you @Lavien) -* Remove extra package +* Enable publishToOpenVSX +* Add Open VSX Registry installation option to README ([#1102](https://github.com/REditorSupport/vscode-R/issues/1102)) +* Use `force=TRUE` when viewing data.frame and list ([#1255](https://github.com/REditorSupport/vscode-R/issues/1255)) +* fixed broken tasks problemMatcher regex ([#1257](https://github.com/REditorSupport/vscode-R/issues/1257)) +* Fix webview: Add webview csp directives for web workers ([#1261](https://github.com/REditorSupport/vscode-R/issues/1261)) +* Add language support for NAMESPACE & .Rbuildignore ([#1221](https://github.com/REditorSupport/vscode-R/issues/1221)) +* Implement help preview for local package(s) ([#1259](https://github.com/REditorSupport/vscode-R/issues/1259)) +* Encoding fix +* Respect RdMacros ([#1266](https://github.com/REditorSupport/vscode-R/issues/1266)) +* Change code block detection to include parentheses ([#1269](https://github.com/REditorSupport/vscode-R/issues/1269)) +* Remove script tags in R v4.2.x help pages ([#1268](https://github.com/REditorSupport/vscode-R/issues/1268)) +* release 2.7.0 -## v0.4.4 +**Full Changelog**: -* Add `Run Source` icon +## 2.6.1 - 2022-10-31 -## v0.4.3 +### Bug Fixes -* Added Data viewer Command(Thank you @Lavien) +* Fix checking `request.viewer` ([#1234](https://github.com/REditorSupport/vscode-R/issues/1234)) -## v0.4.2 +### Other -* Add Source with echo -* Fix keybind +* Tweak settings ([#1235](https://github.com/REditorSupport/vscode-R/issues/1235)) +* Support trailing slash in code-server's URI template ([#1241](https://github.com/REditorSupport/vscode-R/issues/1241)) +* release 2.6.1 -## v0.4.1 +**Full Changelog**: -* Add more shortcut key +## 2.6.0 - 2022-10-11 -## v0.4.0 +### Bug Fixes -* Add shortcut key -* Fix README.md +* Fix empty code examples ([#1194](https://github.com/REditorSupport/vscode-R/issues/1194)) +* Fix help +* Fix help katex support under remote development ([#1217](https://github.com/REditorSupport/vscode-R/issues/1217)) +* Fix use of asExternalUri -## v0.3.9 +### Other -* Fix problem lintr was running other language's files +* Avoid code highlighting in DESCRIPTION files ([#1199](https://github.com/REditorSupport/vscode-R/issues/1199)) +* Avoid .R file lock on windows ([#1192](https://github.com/REditorSupport/vscode-R/issues/1192)) +* devTasks ([#1200](https://github.com/REditorSupport/vscode-R/issues/1200)) +* Add generated .js files to .gitignore +* Bug report template: Minor spelling and formatting adjustments ([#1206](https://github.com/REditorSupport/vscode-R/issues/1206)) +* Implement c_cpp_properties.json file generator fixes #1201 ([#1205](https://github.com/REditorSupport/vscode-R/issues/1205), [#1201](https://github.com/REditorSupport/vscode-R/issues/1201)) +* Refactoring: Strict TypeScript Fix #1208 ([#1209](https://github.com/REditorSupport/vscode-R/issues/1209), [#1208](https://github.com/REditorSupport/vscode-R/issues/1208)) +* [Chore] Remove unused NPM dependencies fix #232 ([#1216](https://github.com/REditorSupport/vscode-R/issues/1216)) +* Support KaTeX in help page viewer ([#1213](https://github.com/REditorSupport/vscode-R/issues/1213)) +* Use asExternalUri instead +* Not trigger preview on file change when rmd is still rendering ([#1219](https://github.com/REditorSupport/vscode-R/issues/1219)) +* release 2.6.0 -## v0.3.8 +**Full Changelog**: -* Improve `Run Selection/Line` (Thank you @Ladvien) - * Added cursorMove after line execution #13 - * Don't pass Rterm comments #14 +## 2.5.3 - 2022-09-06 -## v0.3.7 +### Other -* run lintr on did save automaticaly +* Bump terser from 5.7.0 to 5.14.2 ([#1154](https://github.com/REditorSupport/vscode-R/issues/1154)) +* Remove encoding param from knitting ([#1167](https://github.com/REditorSupport/vscode-R/issues/1167)) +* Reload help pages on help refresh ([#1188](https://github.com/REditorSupport/vscode-R/issues/1188)) +* Upgrade to vscode-languageclient 8.0.2 ([#1173](https://github.com/REditorSupport/vscode-R/issues/1173)) +* release 2.5.3 -## v0.3.6 +**Full Changelog**: -* fix Terminal #7 +## 2.5.2 - 2022-07-15 -## v0.3.5 +### Bug Fixes -* fix syntax +* Workspace viewer fix ([#1150](https://github.com/REditorSupport/vscode-R/issues/1150)) + +### Other + +* Publish to Open VSX Registry ([#1101](https://github.com/REditorSupport/vscode-R/issues/1101)) +* Create issues.yml ([#1061](https://github.com/REditorSupport/vscode-R/issues/1061)) +* Remove nesting: guard clauses ([#1110](https://github.com/REditorSupport/vscode-R/issues/1110)) +* Hide preview env values to prevent accidental deletion ([#1117](https://github.com/REditorSupport/vscode-R/issues/1117)) +* Add file creation to file/newFile ([#1119](https://github.com/REditorSupport/vscode-R/issues/1119)) +* Bump jquery.json-viewer from 1.4.0 to 1.5.0 ([#1123](https://github.com/REditorSupport/vscode-R/issues/1123)) +* Update data viewer column resizing ([#1121](https://github.com/REditorSupport/vscode-R/issues/1121)) +* Click code in help views ([#1138](https://github.com/REditorSupport/vscode-R/issues/1138)) +* Adapt to lintr 3.0 ([#1141](https://github.com/REditorSupport/vscode-R/issues/1141)) +* Whitespace in typescript files ([#1142](https://github.com/REditorSupport/vscode-R/issues/1142)) +* Add `Create .linr` command ([#1112](https://github.com/REditorSupport/vscode-R/issues/1112)) +* Add .yarnrc +* Upgrade to ag-grid-community 28.0.0 ([#1144](https://github.com/REditorSupport/vscode-R/issues/1144)) +* release 2.5.1 +* Disable publish to openvsx +* release 2.5.2 + +**Full Changelog**: + +## 2.5.0 - 2022-05-14 + +### Bug Fixes + +* Fix console err ([#1034](https://github.com/REditorSupport/vscode-R/issues/1034)) +* Fix lintr complain + +### Other + +* add R package build task - build source and build binary like RStudio ([#1029](https://github.com/REditorSupport/vscode-R/issues/1029)) +* Use `bindingIsActive` ([#1031](https://github.com/REditorSupport/vscode-R/issues/1031)) +* guard against evaluation of active bindings ([#1038](https://github.com/REditorSupport/vscode-R/issues/1038), [#1030](https://github.com/REditorSupport/vscode-R/issues/1030)) +* Upgrade `ag-grid-community` to v27.1.0 ([#1049](https://github.com/REditorSupport/vscode-R/issues/1049)) +* Hide smart knit env values to prevent accidental deletion ([#1060](https://github.com/REditorSupport/vscode-R/issues/1060)) +* Add `r.session.data.pageSize` ([#1068](https://github.com/REditorSupport/vscode-R/issues/1068)) +* Bump ansi-regex from 3.0.0 to 3.0.1 ([#1070](https://github.com/REditorSupport/vscode-R/issues/1070)) +* Bump minimist from 1.2.5 to 1.2.6 ([#1069](https://github.com/REditorSupport/vscode-R/issues/1069)) +* Update rGitignore.ts +* Add lsp settings to support disabling prompt and additional libPaths ([#1071](https://github.com/REditorSupport/vscode-R/issues/1071)) +* More choices for code chunk snippet ([#1082](https://github.com/REditorSupport/vscode-R/issues/1082)) +* Take out http from `Insert image` Rmd snippet ([#1084](https://github.com/REditorSupport/vscode-R/issues/1084)) +* Take out http from link snippet ([#1085](https://github.com/REditorSupport/vscode-R/issues/1085)) +* Bump cross-fetch from 3.1.4 to 3.1.5 ([#1087](https://github.com/REditorSupport/vscode-R/issues/1087)) +* Bump ejs from 3.1.6 to 3.1.7 ([#1088](https://github.com/REditorSupport/vscode-R/issues/1088)) +* Use `loadNamespace` ([#1086](https://github.com/REditorSupport/vscode-R/issues/1086)) +* Prompt when no templates found ([#1089](https://github.com/REditorSupport/vscode-R/issues/1089)) +* Update publisher id ([#1093](https://github.com/REditorSupport/vscode-R/issues/1093)) +* R language server and help supports additional libPaths ([#1097](https://github.com/REditorSupport/vscode-R/issues/1097)) +* Move r.libPaths in setting +* Update `r.libPaths` behavior ([#1098](https://github.com/REditorSupport/vscode-R/issues/1098)) +* release 2.5.0 + +**Full Changelog**: + +## 2.4.0 - 2022-03-07 + +### Bug Fixes + +* Fix commented pipe bug ([#988](https://github.com/REditorSupport/vscode-R/issues/988)) +* Fix resolveTask ([#994](https://github.com/REditorSupport/vscode-R/issues/994)) +* Fix incorrect syntax highlighting for variables starting with "function" ([#992](https://github.com/REditorSupport/vscode-R/issues/992), [#982](https://github.com/REditorSupport/vscode-R/issues/982)) + +### Other + +* Use spawn ([#985](https://github.com/REditorSupport/vscode-R/issues/985)) +* WIP: Add problemMatching to task ([#989](https://github.com/REditorSupport/vscode-R/issues/989)) +* R markdown templates ([#984](https://github.com/REditorSupport/vscode-R/issues/984)) +* Preserve selected text in rmd snippet ([#1001](https://github.com/REditorSupport/vscode-R/issues/1001)) +* Provide optional 'code' argument to r.runSelection command ([#1017](https://github.com/REditorSupport/vscode-R/issues/1017)) +* Add Shiny snippets ([#1012](https://github.com/REditorSupport/vscode-R/issues/1012), [#1011](https://github.com/REditorSupport/vscode-R/issues/1011)) +* Add lambda to function-declarations ([#1025](https://github.com/REditorSupport/vscode-R/issues/1025)) +* Enhance workspace viewer ([#1022](https://github.com/REditorSupport/vscode-R/issues/1022)) +* Share httpgd url for LiveShare ([#1026](https://github.com/REditorSupport/vscode-R/issues/1026)) +* Add some useful Rmd snippets ([#1009](https://github.com/REditorSupport/vscode-R/issues/1009)) +* release 2.4.0 + +**Full Changelog**: + +## 2.3.8 - 2022-02-07 + +### Other + +* Avoid rpath quoting ([#981](https://github.com/REditorSupport/vscode-R/issues/981)) +* release 2.3.8 + +**Full Changelog**: + +## 2.3.7 - 2022-02-07 + +### Bug Fixes + +* Fix rmd comment ([#958](https://github.com/REditorSupport/vscode-R/issues/958)) + +### Other + +* correcting typo on command argument (slient instead of silent) ([#954](https://github.com/REditorSupport/vscode-R/issues/954)) +* update the .gitignore file for R ([#949](https://github.com/REditorSupport/vscode-R/issues/949)) +* Add delay before refreshing plots ([#956](https://github.com/REditorSupport/vscode-R/issues/956)) +* Add row limit setting of data viewer and support Apache Arrow Table ([#945](https://github.com/REditorSupport/vscode-R/issues/945)) +* Bump node-fetch from 2.6.1 to 2.6.7 ([#962](https://github.com/REditorSupport/vscode-R/issues/962)) +* set the LANG env when rendering rmarkdown ([#961](https://github.com/REditorSupport/vscode-R/issues/961)) +* should use Strong quote for shell commands ([#964](https://github.com/REditorSupport/vscode-R/issues/964)) +* Add note about required httpgd package version ([#972](https://github.com/REditorSupport/vscode-R/issues/972)) +* update dcf syntax and add support ".lintr" file ([#970](https://github.com/REditorSupport/vscode-R/issues/970)) +* prompt to install languageserver is not available ([#965](https://github.com/REditorSupport/vscode-R/issues/965)) +* [data frame viewer] Add type of column to headerTooltip ([#974](https://github.com/REditorSupport/vscode-R/issues/974)) +* Upgrade ag-grid-community to 26.2.1 ([#975](https://github.com/REditorSupport/vscode-R/issues/975)) +* Activate extension on subfolder ([#979](https://github.com/REditorSupport/vscode-R/issues/979)) +* release 2.3.7 + +**Full Changelog**: -## v0.3.4 +## 2.3.6 - 2022-01-16 -* add "builtin function" from RBox +### Bug Fixes -## v0.3.3 +* Fix syntax file ([#939](https://github.com/REditorSupport/vscode-R/issues/939)) -* New syntax color from R Box -* fix typo(Thank you @Shians) #12 +### Other -## v0.3.1 +* Add raw string tokens ([#922](https://github.com/REditorSupport/vscode-R/issues/922)) +* Support both single and double brackets in code-server's URI template ([#934](https://github.com/REditorSupport/vscode-R/issues/934)) +* Rename `R/session/.Rprofile` to `R/session/profile.R` ([#938](https://github.com/REditorSupport/vscode-R/issues/938)) +* Use taskkill for win32 ([#936](https://github.com/REditorSupport/vscode-R/issues/936)) +* Fixed `"punctuation.section.parens.end.r"` under `"function-parameters"` ([#931](https://github.com/REditorSupport/vscode-R/issues/931)) +* Use `taskkill` for win32 ([#941](https://github.com/REditorSupport/vscode-R/issues/941)) +* release 2.3.6 -* fix Run Selection/Line only executes the first line of file when nothing was selected #9 +**Full Changelog**: -## v0.3.0 +## 2.3.5 - 2021-12-18 -* update lintr behavar +### Bug Fixes -## v0.2.9 +* Fix dcf syntax ([#920](https://github.com/REditorSupport/vscode-R/issues/920)) -* fix lintr on Mac +### Other -## v0.2.8 +* adding devtools tasks to command palette ([#880](https://github.com/REditorSupport/vscode-R/issues/880)) +* RMD - don't set undefined wd ([#914](https://github.com/REditorSupport/vscode-R/issues/914)) +* Use `SIGKILL` to kill help server ([#912](https://github.com/REditorSupport/vscode-R/issues/912)) +* readability adjustments for help pages ([#915](https://github.com/REditorSupport/vscode-R/issues/915)) +* Clean-up child processes on dispose ([#918](https://github.com/REditorSupport/vscode-R/issues/918)) +* release 2.3.5 -* add command `R: Run Selection/Line` +**Full Changelog**: -## v0.2.7 +## 2.3.4 - 2021-11-30 -* add setting `r.source.focus` #5 +### Bug Fixes -## v0.2.6 +* Fix helpserver issue ([#893](https://github.com/REditorSupport/vscode-R/issues/893)) + +### Other + +* Remove quotes from rpath if necessary ([#884](https://github.com/REditorSupport/vscode-R/issues/884)) +* Try different CRAN URLs ([#885](https://github.com/REditorSupport/vscode-R/issues/885)) +* Use `Uri.file` instead of `Uri.parse` ([#888](https://github.com/REditorSupport/vscode-R/issues/888)) +* Update bug_report.md +* Clean up of help related files ([#887](https://github.com/REditorSupport/vscode-R/issues/887)) +* Use httpgd NPM package ([#823](https://github.com/REditorSupport/vscode-R/issues/823)) +* release 2.3.4 + +**Full Changelog**: + +## 2.3.3 - 2021-11-21 + +### Bug Fixes + +* Fix package installation ([#846](https://github.com/REditorSupport/vscode-R/issues/846)) +* Fix detecting yaml frontmatter ([#856](https://github.com/REditorSupport/vscode-R/issues/856)) +* Fix rmd preview chunk colouring ([#867](https://github.com/REditorSupport/vscode-R/issues/867)) + +### Other + +* Add R info to status bar item text and tooltip ([#836](https://github.com/REditorSupport/vscode-R/issues/836)) +* get knit command from settings ([#841](https://github.com/REditorSupport/vscode-R/issues/841)) +* Add support for indented Roxygen ([#847](https://github.com/REditorSupport/vscode-R/issues/847)) +* Syntax highlighting for indented roxygen ([#850](https://github.com/REditorSupport/vscode-R/issues/850)) +* Use new terminal API ([#851](https://github.com/REditorSupport/vscode-R/issues/851)) +* Add backtick to list of quote characters for syntax highlighting. ([#859](https://github.com/REditorSupport/vscode-R/issues/859)) +* Auto refresh help ([#863](https://github.com/REditorSupport/vscode-R/issues/863)) +* Show httpgd plot on attach ([#852](https://github.com/REditorSupport/vscode-R/issues/852)) +* Update lim ([#868](https://github.com/REditorSupport/vscode-R/issues/868)) +* release 2.3.3 + +**Full Changelog**: + +## 2.3.2 - 2021-10-22 + +### Other + +* Httpgd plot viewer respects `r.session.viewers.viewColumn.plot` ([#816](https://github.com/REditorSupport/vscode-R/issues/816)) +* Completely replace `View()` ([#818](https://github.com/REditorSupport/vscode-R/issues/818)) +* Change help cache default ([#819](https://github.com/REditorSupport/vscode-R/issues/819)) +* browser handles `file://` ([#817](https://github.com/REditorSupport/vscode-R/issues/817)) +* Add `r.session.levelOfObjectDetail=Normal` for `max.level=1` ([#815](https://github.com/REditorSupport/vscode-R/issues/815)) +* Update address +* Check workspace folder with both original and real path ([#827](https://github.com/REditorSupport/vscode-R/issues/827)) +* release 2.3.2 + +**Full Changelog**: + +## 2.3.1 - 2021-10-07 + +### Other + +* Support `VSCODE_PROXY_URI` ([#803](https://github.com/REditorSupport/vscode-R/issues/803)) +* Reenable 'unsafe-eval' in script-src CSP ([#805](https://github.com/REditorSupport/vscode-R/issues/805)) +* Use r.session.viewers.viewColumn.helpPanel ([#804](https://github.com/REditorSupport/vscode-R/issues/804)) +* Use cwd in knit process ([#807](https://github.com/REditorSupport/vscode-R/issues/807)) +* Bump version +* release 2.3.1 + +**Full Changelog**: + +## 2.3.0 - 2021-09-23 + +### Bug Fixes + +* Fix RMD requireNamespace ([#784](https://github.com/REditorSupport/vscode-R/issues/784)) +* Fix hljs usage + +### Other + +* Enable rstudioapi by default ([#769](https://github.com/REditorSupport/vscode-R/issues/769)) +* Use unsafe-inline for script-src ([#771](https://github.com/REditorSupport/vscode-R/issues/771)) +* R Markdown Enhancements (Knit Manager) ([#765](https://github.com/REditorSupport/vscode-R/issues/765)) +* (Refactoring) Simplify RMD child process disposal ([#773](https://github.com/REditorSupport/vscode-R/issues/773)) +* Add object length limit ([#778](https://github.com/REditorSupport/vscode-R/issues/778)) +* Write NA as string ([#780](https://github.com/REditorSupport/vscode-R/issues/780)) +* Use R files for background process ([#783](https://github.com/REditorSupport/vscode-R/issues/783)) +* Respect preview output format ([#785](https://github.com/REditorSupport/vscode-R/issues/785)) +* Bump @types/vscode from 1.57.0 to 1.60.0 ([#786](https://github.com/REditorSupport/vscode-R/issues/786)) +* Extend providers to rmd ([#787](https://github.com/REditorSupport/vscode-R/issues/787)) +* Bump nth-check from 2.0.0 to 2.0.1 ([#795](https://github.com/REditorSupport/vscode-R/issues/795)) +* Update vscode and ag-grid version +* Update dependencies +* Update highlight.js version +* release 2.3.0 + +**Full Changelog**: + +## 2.2.0 - 2021-08-21 + +### Bug Fixes + +* Fix date filter in data viewer ([#736](https://github.com/REditorSupport/vscode-R/issues/736)) +* Fix README +* Fix issues with c() in function args ([#751](https://github.com/REditorSupport/vscode-R/issues/751), [#713](https://github.com/REditorSupport/vscode-R/issues/713)) + +### Other + +* Check conflict extension ([#733](https://github.com/REditorSupport/vscode-R/issues/733)) +* Rename liveshare folder to liveShare ([#738](https://github.com/REditorSupport/vscode-R/issues/738)) +* Viewer fix: invalid html_widget resource paths ([#739](https://github.com/REditorSupport/vscode-R/issues/739)) +* Accessing VS Code settings in R ([#743](https://github.com/REditorSupport/vscode-R/issues/743)) +* Use .DollarNames with default pattern ([#750](https://github.com/REditorSupport/vscode-R/issues/750)) +* Handle error in capture_str ([#756](https://github.com/REditorSupport/vscode-R/issues/756)) +* Add icons to webviews ([#759](https://github.com/REditorSupport/vscode-R/issues/759)) +* release 2.2.0 + +**Full Changelog**: + +## 2.1.0 - 2021-07-20 + +### Bug Fixes + +* Fix README links +* Fix typo in url +* Fix typo + +### Other + +* Minor workspace-related changes ([#672](https://github.com/REditorSupport/vscode-R/issues/672)) +* Update README ([#669](https://github.com/REditorSupport/vscode-R/issues/669)) +* Enable r.sessionWatcher by default +* Update previewDataframe and previewEnvironment +* Enable r.sessionWatcher by default ([#670](https://github.com/REditorSupport/vscode-R/issues/670)) +* Add customization options to plot viewer ([#678](https://github.com/REditorSupport/vscode-R/issues/678)) +* Catch LiveShare API errors ([#679](https://github.com/REditorSupport/vscode-R/issues/679)) +* Small Plot Viewer adjustments ([#681](https://github.com/REditorSupport/vscode-R/issues/681)) +* Integer syntax supports e.g. 1e2L +* Integer syntax supports e.g. 1e2L ([#683](https://github.com/REditorSupport/vscode-R/issues/683)) +* Change License owner +* Update url and author +* Update url and author ([#694](https://github.com/REditorSupport/vscode-R/issues/694)) +* Preview R Markdown files via background process ([#692](https://github.com/REditorSupport/vscode-R/issues/692)) +* RMD Preview fixes ([#699](https://github.com/REditorSupport/vscode-R/issues/699)) +* Update r.rmarkdown.codeLensCommands ([#707](https://github.com/REditorSupport/vscode-R/issues/707)) +* Remove show plot history command ([#706](https://github.com/REditorSupport/vscode-R/issues/706)) +* Add full window mode for plots ([#709](https://github.com/REditorSupport/vscode-R/issues/709)) +* Use ag-grid in data viewer ([#708](https://github.com/REditorSupport/vscode-R/issues/708)) +* Integrate vscode-r-lsp ([#695](https://github.com/REditorSupport/vscode-R/issues/695)) +* prerelease 2.1.0 +* release 2.1.0 +* release 2.1.0 + +**Full Changelog**: + +## 2.0.0 - 2021-06-12 + +### Bug Fixes + +* Fix getCurrentChunk and use chunks.find for most cases + +### Other + +* Use .DollarNames for object with class in completion +* Use `.DollarNames` for object with class in completion ([#660](https://github.com/REditorSupport/vscode-R/issues/660)) +* Code cells in .R files +* Update rmarkdown.ts +* Remove unused languages +* Code cells in .R files ([#662](https://github.com/REditorSupport/vscode-R/issues/662)) +* Squash bugs +* Change source & knit icons +* Jump to cursor +* Remove image files not used +* rmarkdown bug squashing and minor changes ([#663](https://github.com/REditorSupport/vscode-R/issues/663)) +* Bump css-what from 5.0.0 to 5.0.1 +* Bump css-what from 5.0.0 to 5.0.1 ([#664](https://github.com/REditorSupport/vscode-R/issues/664)) +* Bugfix +* LiveShare Functionality ([#626](https://github.com/REditorSupport/vscode-R/issues/626)) +* prerelease 2.0.0 ([#667](https://github.com/REditorSupport/vscode-R/issues/667)) + +**Full Changelog**: + +## 1.6.8 - 2021-05-31 + +### Bug Fixes + +* Fix typo in internal file name +* Fix Webview Identification Function ([#650](https://github.com/REditorSupport/vscode-R/issues/650)) +* Fix bugs in helpviewer ([#658](https://github.com/REditorSupport/vscode-R/issues/658)) + +### Other + +* Fix typo in internal file name ([#643](https://github.com/REditorSupport/vscode-R/issues/643)) +* Revert syntax for lambda +* Revert syntax for lambda ([#657](https://github.com/REditorSupport/vscode-R/issues/657)) +* version 1.6.8 + +**Full Changelog**: + +## 1.6.7 - 2021-05-31 + +### Bug Fixes + +* Fix replacing base::.External.graphics +* Fix Github actions +* fix highlight.js + +### Other + +* Adding markdownlint on extension.json and GitHub Actions ([#591](https://github.com/REditorSupport/vscode-R/issues/591)) +* Fix replacing `base::.External.graphics` ([#625](https://github.com/REditorSupport/vscode-R/issues/625)) +* Integrate httpgd ([#620](https://github.com/REditorSupport/vscode-R/issues/620)) +* Minor fix of README +* Prefer rPath from PATH +* Prefer rPath from PATH ([#649](https://github.com/REditorSupport/vscode-R/issues/649)) +* Improve development workflow ([#641](https://github.com/REditorSupport/vscode-R/issues/641)) +* Don't run chunks with eval = FALSE. Fixes #651 ([#651](https://github.com/REditorSupport/vscode-R/issues/651)) +* Don't run chunks with eval = FALSE. Fixes #651 ([#653](https://github.com/REditorSupport/vscode-R/issues/653), [#651](https://github.com/REditorSupport/vscode-R/issues/651)) +* Add pipe and lambda to syntax +* Update doesLineEndInOperator +* Update R syntax ([#647](https://github.com/REditorSupport/vscode-R/issues/647)) +* prerelease 1.6.7 +* update changelog +* Prerelease 1.6.7 ([#655](https://github.com/REditorSupport/vscode-R/issues/655)) + +**Full Changelog**: + +## 1.6.6 - 2021-04-11 + +### Bug Fixes + +* Fix leading/trailing newlines +* fix package volunerability + +### Other + +* Update vscode engine +* Update vscode engine ([#586](https://github.com/REditorSupport/vscode-R/issues/586)) +* Satisfy markdownlint +* Satisfy markdownlint ([#587](https://github.com/REditorSupport/vscode-R/issues/587)) +* Initial Workspace Viewer str() functionality (#577) +* Initial Workspace Viewer str() functionality ([#583](https://github.com/REditorSupport/vscode-R/issues/583)) +* Thread execute argument through to term.sendText() ([#585](https://github.com/REditorSupport/vscode-R/issues/585)) +* Remove object.size +* Use cache to store object size for objects in globalenv +* Add option vsc.show_object_size +* Update object size when length changes +* Update getSizeString() to be consistent with format() in R +* Being more conservative to call object.size() in task callback ([#581](https://github.com/REditorSupport/vscode-R/issues/581)) +* Send code to debug repl +* Send code to debug repl ([#582](https://github.com/REditorSupport/vscode-R/issues/582)) +* Clarify R path error messages ([#596](https://github.com/REditorSupport/vscode-R/issues/596)) +* Add tasks Check, Document, Install, Test ([#603](https://github.com/REditorSupport/vscode-R/issues/603)) +* shim the rstudioapi if it has already been loaded +* make lintr happy +* shim the rstudioapi if it has already been loaded ([#610](https://github.com/REditorSupport/vscode-R/issues/610)) +* Clarify error messages ([#607](https://github.com/REditorSupport/vscode-R/issues/607)) +* version 1.6.6 + +### Refactor + +* Refactor R code ([#602](https://github.com/REditorSupport/vscode-R/issues/602)) + +**Full Changelog**: + +## 1.6.5 - 2021-03-15 + +### Bug Fixes + +* Fix get_timestamp() so that it will not be affected by e.g. options(digits=3) ([#550](https://github.com/REditorSupport/vscode-R/issues/550)) +* Fix so code can be run after creating terminal +* Fix #572 ([#572](https://github.com/REditorSupport/vscode-R/issues/572)) + +### Other + +* Change workspace tooltip ([#544](https://github.com/REditorSupport/vscode-R/issues/544)) +* add option vsc.hover.str.max.level ([#545](https://github.com/REditorSupport/vscode-R/issues/545)) +* Refactoring and implementation of webviewPanelSerializer ([#556](https://github.com/REditorSupport/vscode-R/issues/556)) +* Scroll to bottom after running a command ([#559](https://github.com/REditorSupport/vscode-R/issues/559)) +* Add option to keep terminal hidden +* Clarify r.source.focus options in description +* Add option to keep terminal hidden after running code ([#566](https://github.com/REditorSupport/vscode-R/issues/566)) +* Check rTerm is defined before showing +* Fix so code can be run after creating terminal ([#567](https://github.com/REditorSupport/vscode-R/issues/567)) +* Move `r.runSource` and `r.knitRmd` to `editor/title/run` (Fix #572) ([#573](https://github.com/REditorSupport/vscode-R/issues/573), [#572](https://github.com/REditorSupport/vscode-R/issues/572)) +* Add links to help pages in hover +* Improve the formatting +* Add links to help pages in hover ([#578](https://github.com/REditorSupport/vscode-R/issues/578)) +* version 1.6.5 +* update vscode engine + +**Full Changelog**: + +## 1.6.4 - 2021-02-01 + +### Other + +* Better error message when reading aliases ([#518](https://github.com/REditorSupport/vscode-R/issues/518)) +* Keep promises and active bindings in globalenv ([#521](https://github.com/REditorSupport/vscode-R/issues/521)) +* (Maybe) Fix Aliases ([#526](https://github.com/REditorSupport/vscode-R/issues/526)) +* add sendToConsole to rstudioapi emulation ([#535](https://github.com/REditorSupport/vscode-R/issues/535)) +* Add updatePackage command ([#532](https://github.com/REditorSupport/vscode-R/issues/532)) +* Add initial pipeline completion support ([#530](https://github.com/REditorSupport/vscode-R/issues/530)) +* Add function to open help for selected text ([#531](https://github.com/REditorSupport/vscode-R/issues/531)) +* Preserve focus when opening help view ([#541](https://github.com/REditorSupport/vscode-R/issues/541)) +* update packages +* version 1.6.4 +* Prerelease version 1.6.4 ([#542](https://github.com/REditorSupport/vscode-R/issues/542)) +* version 1.6.4 + +### Refactor + +* Refactor extension.ts ([#525](https://github.com/REditorSupport/vscode-R/issues/525)) +* refactor import statements + +**Full Changelog**: + +## 1.6.3 - 2021-01-01 + +### Other + +* Implement R workspace viewer ([#476](https://github.com/REditorSupport/vscode-R/issues/476)) +* add a new collaborator +* Conditionally show view ([#487](https://github.com/REditorSupport/vscode-R/issues/487)) +* Enable find widget in WebViews ([#490](https://github.com/REditorSupport/vscode-R/issues/490)) +* Find in topic (help panel) #463 ([#488](https://github.com/REditorSupport/vscode-R/issues/488)) +* Disable alwaysShow for addin items ([#491](https://github.com/REditorSupport/vscode-R/issues/491)) +* Only show R menu items in R view ([#493](https://github.com/REditorSupport/vscode-R/issues/493)) +* Modify pre-release action ([#492](https://github.com/REditorSupport/vscode-R/issues/492)) +* Add browser WebView command buttons ([#494](https://github.com/REditorSupport/vscode-R/issues/494)) +* typo fix +* typo fix ([#500](https://github.com/REditorSupport/vscode-R/issues/500)) +* Improve help view ([#502](https://github.com/REditorSupport/vscode-R/issues/502)) +* releaseAction ([#505](https://github.com/REditorSupport/vscode-R/issues/505)) +* Strict null checking in help related code ([#507](https://github.com/REditorSupport/vscode-R/issues/507)) +* WIP: Pre fixing for version 1.6.3 ([#508](https://github.com/REditorSupport/vscode-R/issues/508)) +* version 1.6.3 + +**Full Changelog**: + +## 1.6.2 - 2020-12-06 + +### Bug Fixes + +* Fix bug that would leave background R processes running ([#475](https://github.com/REditorSupport/vscode-R/issues/475)) +* Fix whole of style (Extends #361) ([#474](https://github.com/REditorSupport/vscode-R/issues/474)) + +### Other + +* Add pre-release ([#468](https://github.com/REditorSupport/vscode-R/issues/468)) +* Improve Help Panel ([#470](https://github.com/REditorSupport/vscode-R/issues/470)) +* RMarkdown: Add run & navigation commands. More customization. Refactor. ([#465](https://github.com/REditorSupport/vscode-R/issues/465)) +* Fixes #443 ([#462](https://github.com/REditorSupport/vscode-R/issues/462)) +* Reorganize helppanel, add `?` function ([#477](https://github.com/REditorSupport/vscode-R/issues/477)) +* Update README for #465 +* Update README for #465 ([#480](https://github.com/REditorSupport/vscode-R/issues/480)) +* Improve style of help pages ([#481](https://github.com/REditorSupport/vscode-R/issues/481)) +* Modify config ([#467](https://github.com/REditorSupport/vscode-R/issues/467)) +* update devreplay rules +* back the previous custom rules +* version 1.6.2 + +**Full Changelog**: + +## 1.6.1 - 2020-11-24 + +### Bug Fixes + +* Fix type in help panel path config +* Fix lint: missing semicolon +* Fix checking workspaceFolders in rHelpProviderOptions + +### Other + +* Don't use rterm as fallback for help panel R path +* Fix typo in help panel path config ([#457](https://github.com/REditorSupport/vscode-R/issues/457)) +* New feature r.runFromLineToEnd +* New feature r.runFromLineToEnd ([#448](https://github.com/REditorSupport/vscode-R/issues/448)) +* Also check length +* Fix checking workspaceFolders in rHelpProviderOptions ([#456](https://github.com/REditorSupport/vscode-R/issues/456)) +* Highlight all chunks +* Highlight all chunks ([#453](https://github.com/REditorSupport/vscode-R/issues/453)) +* Add GitHub Action for release +* Add GitHub Action for release ([#449](https://github.com/REditorSupport/vscode-R/issues/449)) +* Add Rmd fenced_block_* for julia, python, etc +* Rmd fenced block syntax highlighting for julia, python, etc ([#460](https://github.com/REditorSupport/vscode-R/issues/460)) +* Update README: Add options(vsc.helpPanel = ...) +* Update README: Add options(vsc.helpPanel = ...) ([#461](https://github.com/REditorSupport/vscode-R/issues/461)) +* version 1.6.1 + +**Full Changelog**: + +## 1.6.0 - 2020-11-21 + +### Bug Fixes + +* Fix usage of GlobPattern +* Fix leading slash in tmpDir path +* Fix showWebView +* Fix build.yml +* Fix handling group_df in dataview_table +* fix extension tslint to eslint +* fix #265 ([#265](https://github.com/REditorSupport/vscode-R/issues/265)) +* Fix main.yml +* Fix eslint +* fix vlunerability +* fix vscode version +* Fix code formatting +* Fix main.yml +* Fix .lintr +* Fix .Rprofile line length +* fix package dependencies +* fix package-lock.json +* Fix typo in showWebView +* fix devreplay error +* Fix lintr github action +* Fix new test cases +* Fix outFiles in launch.json +* Fix View empty environment +* Fix typo in README.md +* Fix homedir on Windows +* fix for new conventions +* fix config title r to R +* Fix viewer and page_viewer url +* Fix plot viewer +* Fix typo +* Fix typo +* fix dependencies +* Fix previewDataframe for 2+ letter variables +* Fix so rTerm is undefined when deleting terminal +* fix package volunerability +* Fix more functions to use runTextInTerm +* fix package dependencies +* fix webpack for the new webpack interface +* Fix linting message +* fix and enhance navigateToFile +* fix toString -> toString() + +### Other + +* version 1.2.2 +* Update FAQ +* Add logging to session watcher +* Add more logging to session watcher ([#208](https://github.com/REditorSupport/vscode-R/issues/208)) +* updateResponse only handles response when responseLineCount increases +* updateResponse only handles response when responseLineCount changes +* Avoid duplicate handling of response update ([#211](https://github.com/REditorSupport/vscode-R/issues/211)) +* first tslint cleanup +* Update activationEvents +* Update activationEvents +* Update activationEvents ([#224](https://github.com/REditorSupport/vscode-R/issues/224)) +* Add syntax highlighting for R code in Rcpp comment +* Add syntax highlighting for R code in Rcpp comment ([#225](https://github.com/REditorSupport/vscode-R/issues/225)) +* Fixed the function snippet (Issue #230) +* Fixed the function snippet (Issue #230) ([#231](https://github.com/REditorSupport/vscode-R/issues/231)) +* add collaborator +* version 1.2.3 +* update vscode +* Add statement of languageserver features to bug_report.md +* Add statement of languageserver features to bug report template ([#229](https://github.com/REditorSupport/vscode-R/issues/229)) +* string interpolation command runner +* add runCommandWithPath and clean +* swtich to '$$' replacement target, editor paths unquoted +* Add command runner function doco to README +* add quotes to runCommandWithPath example +* handle unsaved and unititled files +* global replace enabled for $$ +* use escaped double quotes in keybinding example +* Add configurable command runner functions ([#237](https://github.com/REditorSupport/vscode-R/issues/237)) +* Minor refine README.md +* Fix leading slash in tmpDir path ([#221](https://github.com/REditorSupport/vscode-R/issues/221)) +* Change platform gui in init.R +* Change .Platform$GUI to vscode on session start ([#234](https://github.com/REditorSupport/vscode-R/issues/234)) +* Inject R Markdown features into Markdown grammar +* Remove non-bracket code chunks from R Markdown grammar +* Inject R Markdown features into Markdown grammar ([#228](https://github.com/REditorSupport/vscode-R/issues/228)) +* version 1.2.4 +* version 1.2.5 +* Check untitled document and save result before running command +* Update code +* Check untitled document and save result before running command ([#239](https://github.com/REditorSupport/vscode-R/issues/239)) +* Fix showWebView ([#246](https://github.com/REditorSupport/vscode-R/issues/246)) +* version 1.2.6 +* Add lint workflows +* Rename lint action name +* Combine lint workflows +* Use GitHub Actions for linting ([#251](https://github.com/REditorSupport/vscode-R/issues/251)) +* Add build.yml +* Combine to single main.yml +* Refine main.yml +* Use GitHub Actions to build extension ([#253](https://github.com/REditorSupport/vscode-R/issues/253)) +* Use Windows registry to find R path +* Refine getRpath +* Update package dependencies +* Add logging +* Use async getRpath() +* Update README.md +* Use Windows registry to find R path ([#252](https://github.com/REditorSupport/vscode-R/issues/252)) +* Fix handling grouped_df in dataview_table ([#248](https://github.com/REditorSupport/vscode-R/issues/248)) +* try to adding eslint +* version 1.2.7 +* try to adding eslint ([#254](https://github.com/REditorSupport/vscode-R/issues/254)) +* Add wiki link on README +* support single quote +* add backtick support +* Support single quote (Fix #260) ([#264](https://github.com/REditorSupport/vscode-R/issues/264), [#260](https://github.com/REditorSupport/vscode-R/issues/260)) +* Use eslint in GitHub Actions +* Update package-lock.json +* Use setup-node +* Use eslint in GitHub Actions ([#266](https://github.com/REditorSupport/vscode-R/issues/266)) +* Add languages embedded in markdown +* Add surround support for R Markdown files +* Add backtick support for R documentation files +* Remove backtick auto-closing pair for R Markdown +* Add R Markdown surround and frontmatter comments ([#269](https://github.com/REditorSupport/vscode-R/issues/269)) +* update eslint rules +* back tsconfig and remove tslint +* version 1.2.8 +* Update lintr +* Fix R code formatting according to linting results ([#278](https://github.com/REditorSupport/vscode-R/issues/278)) +* Use env to specify vsix file in github actions build +* Make rebind also work in attached packages +* Make rebind also work with attached packages ([#268](https://github.com/REditorSupport/vscode-R/issues/268)) +* Make plot update more smart using magic null dev size +* Refine code +* Make plot update smarter using magic null dev size ([#274](https://github.com/REditorSupport/vscode-R/issues/274)) +* lintr action error on any non-empty lintr result +* Fix lintr action ([#280](https://github.com/REditorSupport/vscode-R/issues/280)) +* Remove --no-site-file from default r.rterm.option +* Remove --no-site-file from default r.rterm.option ([#284](https://github.com/REditorSupport/vscode-R/issues/284)) +* Source Rprofile.site at last +* Remove Rprofile.site +* Update .Rprofile +* Update .Rprofile +* Improve .Rprofile ([#282](https://github.com/REditorSupport/vscode-R/issues/282)) +* update vscode engine +* Change so setting changes take effect immediately ([#301](https://github.com/REditorSupport/vscode-R/issues/301)) +* version 1.3.0 +* update contributing.md +* Refine .Rprofile +* Refine .Rprofile to remove unnecessary printing in R startup message. ([#303](https://github.com/REditorSupport/vscode-R/issues/303)) +* Change runTextInTerm to string from string[] +* Change signatures to string from string[] +* Simplify removeCommentedLines +* Use bracketed paste for more commands #294 ([#305](https://github.com/REditorSupport/vscode-R/issues/305)) +* Add command Run from Beginning to Line +* Add command Run from Beginning to Line ([#290](https://github.com/REditorSupport/vscode-R/issues/290)) +* add devreplay +* Making the vscode-r original coding conventions ([#308](https://github.com/REditorSupport/vscode-R/issues/308)) +* merge 3 repeated keyword.operator.comparison.r in r.json file. +* merge 3 repeated keyword.operator.comparison.r in r.json file. ([#311](https://github.com/REditorSupport/vscode-R/issues/311)) +* Fix typo in showWebView ([#310](https://github.com/REditorSupport/vscode-R/issues/310)) +* No remove blank or comment lines +* Remove removeCommentedLines as unused +* Remove checkForBlankOrComment as unused +* No remove blank or comment lines ([#313](https://github.com/REditorSupport/vscode-R/issues/313)) +* Add vscode-test dependency +* Update package-lock.json after adding vscode-test +* Update scripts for test and pretest +* Add runTest.ts and index.ts for new test format +* Remove old format test index.ts +* Move test file +* Update path +* Change double quotes to single quotes +* Update tsconfig.json for new test format +* Remove launch configuration Launch Tests +* Ignore .vscode-test +* Add test to github actions +* Refine test github action +* Migrate to vscode-test #315 ([#317](https://github.com/REditorSupport/vscode-R/issues/317)) +* Fix lintr github action ([#319](https://github.com/REditorSupport/vscode-R/issues/319)) +* extendSelection only handles brackets outside quotes +* Add test cases for extendSelection and fix formatting +* Refine condition flow +* Add test/*.ts to eslint +* Refine eslint github action +* extendSelection only handles brackets outside quotes ([#314](https://github.com/REditorSupport/vscode-R/issues/314)) +* Add r.runSelectionRetainCursor +* Add r.runSelectionRetainCursor ([#325](https://github.com/REditorSupport/vscode-R/issues/325)) +* Add launch tests to launch.json +* Update launch.json +* Update tasks.json and launch.json +* Update launch.json and tasks.json +* Update launch.json and tasks.json +* Add launch tests to launch.json ([#320](https://github.com/REditorSupport/vscode-R/issues/320)) +* supress auto-opening quote in roxygen comment +* supress auto-opening quote in roxygen comment ([#328](https://github.com/REditorSupport/vscode-R/issues/328)) +* converted Rmd file to json format +* converted Rcpp language file to json +* converted RD language file to json +* converted indentation to spaces to be consistent +* Convert language files to Json ([#333](https://github.com/REditorSupport/vscode-R/issues/333)) +* exposure send text delay as a parameter +* changed description of rtermsenddelay; stopped multiple config reads +* expose send text delay as a parameter ([#336](https://github.com/REditorSupport/vscode-R/issues/336)) +* Added functionality to switch to active R terminal +* fixed rTermNameOptions typo +* updated to select last created Rterminal +* Added functionality to switch to an existing R terminal ([#338](https://github.com/REditorSupport/vscode-R/issues/338)) +* added functionality to search PATH in mac/linux +* updated to auto detect R on windows as well +* added missing argument type +* Enable default R location to be used on mac/linux if none is supplied ([#340](https://github.com/REditorSupport/vscode-R/issues/340)) +* add dcf +* add syntax highlight for DESCRIPTION and .Rproj ([#342](https://github.com/REditorSupport/vscode-R/issues/342)) +* Define lint in package.json and use it in GitHub Actions +* Update main.yml +* Define lint in package.json and use it in GitHub Actions ([#344](https://github.com/REditorSupport/vscode-R/issues/344)) +* version 1.4.0 +* Fix View empty environment ([#350](https://github.com/REditorSupport/vscode-R/issues/350)) +* Use fs.watch instead of vscode.FileSystemWatcher +* Only handle request of R session started from workspace folders or subfolders +* Use request lock file to avoid partial change +* Use plot.lock and globalenv.lock +* Update README.md +* Update init.R +* Not source init.R in RStudio +* Use fs.watch instead of vscode.FileSystemWatcher ([#348](https://github.com/REditorSupport/vscode-R/issues/348)) +* Improve getBrowserHtml +* Improve getBrowserHtml ([#353](https://github.com/REditorSupport/vscode-R/issues/353)) +* Change runSelectionInActiveTerm effect to warning +* Change runSelectionInActiveTerm effect to warning ([#351](https://github.com/REditorSupport/vscode-R/issues/351)) +* update/remove packages +* version 1.4.1 +* Initial rewrite of init.R +* Rewrite session watcher +* Update init.R +* Update options +* Update options +* Update options +* Use viewer === false to open externally +* Clean up and use .vsc.attach() +* Support htmlwidget input and title in viewer and page_viewer +* Rename parseResult to request +* normalizePath in webview +* Session watcher options ([#359](https://github.com/REditorSupport/vscode-R/issues/359)) +* Remove single quote from doesLineEndInOperator +* Add test cases for extendSelection with quotes +* Remove single quote from doesLineEndInOperator ([#357](https://github.com/REditorSupport/vscode-R/issues/357)) +* update CHANGELOG for following #359 +* Update changelog +* Update changelog ([#362](https://github.com/REditorSupport/vscode-R/issues/362)) +* version 1.4.2 +* update change log links +* Add session watcher functions and options to README +* Update README +* Update README +* Update README +* Update README +* Update README +* Update README +* Fix plot viewer ([#365](https://github.com/REditorSupport/vscode-R/issues/365)) +* Minor update readme +* Accept all dir when no workspace folder is open +* Only accept session started from home folder +* Handle undefined workspace folders ([#367](https://github.com/REditorSupport/vscode-R/issues/367)) +* On Mac & Linux, rely on PATH being set up +* Update package.json +* On Mac & Linux, rely on PATH being set up ([#374](https://github.com/REditorSupport/vscode-R/issues/374)) +* version 1.4.3 +* update webpack options +* update mocha option +* version 1.4.4 +* Fix previewDataframe for 2+ letter variables ([#390](https://github.com/REditorSupport/vscode-R/issues/390)) +* fixed typo and added sep choice +* fixed typo and added sep choice ([#397](https://github.com/REditorSupport/vscode-R/issues/397)) +* Fix so rTerm is undefined when deleting terminal ([#403](https://github.com/REditorSupport/vscode-R/issues/403)) +* Restore R_PROFILE_USER +* Restore R_PROFILE_USER ([#392](https://github.com/REditorSupport/vscode-R/issues/392)) +* Remove Ctrl + 1, 2, 3, 4, 5 shortcuts +* Update CHANGELOG for removing Ctrl + 1, 2, 3, 4, 5 +* Remove Ctrl + 1, 2, 3, 4, 5 shortcuts ([#401](https://github.com/REditorSupport/vscode-R/issues/401)) +* version 1.4.5 +* Check url in browser +* Use path_to_uri in browser +* Check url in browser ([#406](https://github.com/REditorSupport/vscode-R/issues/406)) +* Remove active parameter from chooseTerminal() +* Remove term parameter from runTextInTerm() +* Remove chooseTerminalAndSendText() +* Remove term parameter from runSelectionInTerm() +* Remove command runSelectionInActiveTerm +* Remove trailing whitespace +* Remove command Run Selection/Line in Active Terminal ([#409](https://github.com/REditorSupport/vscode-R/issues/409)) +* Remove Run in Active Terminal from README +* Remove Run in Active Terminal from README ([#413](https://github.com/REditorSupport/vscode-R/issues/413)) +* version 1.4.6 +* update change log +* Recommend radian in README +* Recommend radian in README ([#420](https://github.com/REditorSupport/vscode-R/issues/420)) +* RStudio Addin Support ([#408](https://github.com/REditorSupport/vscode-R/issues/408)) +* remove winattr for the character error +* add missed file +* version 1.5.0 +* make update addin registry a safe call +* tested glitch protection in addin.dcf read +* supply actual version numbers to keep {cli} happy +* require rstudioapi emulation be enabled via option +* note about option in README +* move tryCatch to dcf read/parse +* move guards for vsc.rstudioapi +* move rstudioapi_enabled to init.R +* lintr fixes +* don't need active rTerm +* Fix issues in rstudioapi emulation ([#422](https://github.com/REditorSupport/vscode-R/issues/422)) +* Rename init functions +* Rename init functions ([#425](https://github.com/REditorSupport/vscode-R/issues/425)) +* version 1.5.1 +* Use help_type='html' only when unspecified +* Print url before sending browser request to trigger auto port-forwarding +* Improve handling html help ([#427](https://github.com/REditorSupport/vscode-R/issues/427)) +* fix and enhance navigateToFile ([#430](https://github.com/REditorSupport/vscode-R/issues/430)) +* Enhance R markdown support ([#429](https://github.com/REditorSupport/vscode-R/issues/429)) +* version 1.5.2 +* Add runAboveChunks command +* Drop empty chunks in runChunksInTerm +* Send trimmed text from chunks +* Add runAboveChunks command ([#434](https://github.com/REditorSupport/vscode-R/issues/434)) +* patform independent content string splitting +* nicer error when can't find list of rstudio addins +* platform independent content string splitting ([#436](https://github.com/REditorSupport/vscode-R/issues/436)) +* Merge remote-tracking branch 'upstream/master' +* remove full stop +* Friendly error message when trying to launch addin picker and vsc.rstudioapi = FALSE ([#441](https://github.com/REditorSupport/vscode-R/issues/441)) +* Send code at EOF appends new line +* Send code at EOF appends new line ([#444](https://github.com/REditorSupport/vscode-R/issues/444)) +* Add terminal information to chooseTerminal error ([#447](https://github.com/REditorSupport/vscode-R/issues/447)) +* Integrate help view from vscode-R-help ([#433](https://github.com/REditorSupport/vscode-R/issues/433)) +* version 1.6.0 + +### Refactor + +* refactor + +### Styling + +* style fix + +**Full Changelog**: + +## 1.2.1-20200124-625ae35 - 2020-01-24 + +### Bug Fixes + +* fix PositionNeg implementation +* fix depencency +* fix defaul runSelection +* fix SetFocus to choose terminal +* Fix to allow creation of first terminal +* Fix check for Excel Viewer extension +* fix for valunerability +* Fix for R markdown config +* Fix Preview Environment for multi-class objects +* Fix Preview Environment for variable x +* fix for tslint +* fix package dependencies +* fix behaviour when workplacefolders is Undefiend +* fix LICENSE to MIT +* fix vlunerable packages +* Fix function call closing bracket highlight +* Fix typo in init.R +* Fix for tslint +* Fix error message +* Fix bootstrap dependency +* Fix #168 ([#168](https://github.com/REditorSupport/vscode-R/issues/168)) +* Fix session watcher init.R path on Windows +* Fix typo +* Fix typo +* fix style +* Fix type check for completion of function +* Fix usage of CancellationToken +* Fix WebView Uri replacing +* Fix dataview_table handling single row data + +### Other + +* Update ISSUE_TEMPLATE.md +* add wordPattern (fixed #75) ([#75](https://github.com/REditorSupport/vscode-R/issues/75)) +* version 0.6.2 +* sorry, I can not continue support +* version 1.0.1 +* version 1.0.2 +* Update issue templates +* Create PULL_REQUEST_TEMPLATE.md +* Update bug_report.md +* typo +* Preview Dataframe checks for whitespace +* Preview Dataframe command works again +* Fix Preview Dataframe command #67 ([#97](https://github.com/REditorSupport/vscode-R/issues/97)) +* version 1.0.3 +* Adapt runSelection to use RCommands as Shortcut +* Adapt runSelection to use RCommands as Shortcut ([#101](https://github.com/REditorSupport/vscode-R/issues/101)) +* version 1.0.4 +* version 1.0.5 +* Add runSelectionInActiveTerm +* Add first terminal check to chooseTerminal +* Add animation showing SSH use +* Add runSelectionInActiveTerm command #80 #102 ([#104](https://github.com/REditorSupport/vscode-R/issues/104)) +* miss to merge +* version 1.0.7 +* .gitignore is now working +* version 1.0.8 +* Fix check for Excel Viewer extension #96 ([#108](https://github.com/REditorSupport/vscode-R/issues/108)) +* add gc-excel installer +* version 1.0.9 +* version 1.1.0 +* Fix Preview Environment for multi-class objects #111 ([#113](https://github.com/REditorSupport/vscode-R/issues/113)) +* Fix Preview Environment for variable x #111 ([#115](https://github.com/REditorSupport/vscode-R/issues/115)) +* version 1.1.1 +* version 1.1.2 +* Add bracketed paste mode option +* Do not send blank lines to console +* Fix send code for newlines and Radian #114 #117 ([#119](https://github.com/REditorSupport/vscode-R/issues/119)) +* Added Rmd knit shortcut +* Add knit to PDF command +* Added HTMl and all as Knit options +* Remove icons for all but Knit default +* RMarkdown knit support ([#122](https://github.com/REditorSupport/vscode-R/issues/122)) +* version 1.1.3 +* Fixed spelling, improved formatting +* Fixed spelling, improved formatting ([#129](https://github.com/REditorSupport/vscode-R/issues/129)) +* Automatically comment new lines in roxygen sections +* Automatically comment new lines in roxygen sections #124 ([#130](https://github.com/REditorSupport/vscode-R/issues/130)) +* Do not send blank lines ending in CRLF to console +* Fix send code for newlines on Windows #114 ([#125](https://github.com/REditorSupport/vscode-R/issues/125)) +* add roxygen comments +* Add auto-completion of roxygen tags +* Add auto-completion of roxygen tags #128 ([#131](https://github.com/REditorSupport/vscode-R/issues/131)) +* Change cursorMove +* Change cursorMove to wrappedLineFirstNonWhitespaceCharacter ([#127](https://github.com/REditorSupport/vscode-R/issues/127)) +* version 1.1.4 +* replace deprecated function +* Remove redundant functions +* Move send text functions into rTerminal +* Add alwaysUseActiveTerminal setting +* Add alwaysUseActiveTerminal to README, templates +* Add alwaysUseActiveTerminal setting #123 ([#133](https://github.com/REditorSupport/vscode-R/issues/133)) +* version 1.1.5 +* Show r.term.option value in settings UI +* Show r.term.option value in settings UI ([#136](https://github.com/REditorSupport/vscode-R/issues/136)) +* fix behaviour when workplacefolders is Undefiend ([#138](https://github.com/REditorSupport/vscode-R/issues/138)) +* refactoring +* version 1.1.6 +* remove duplicated quote #139 +* version 1.1.7 +* Use word under cursor for previewDataframe, nrow +* Apply functions once instead of to each line +* remove extra calling +* Use word under cursor for previewDataframe, nrow #137 ([#141](https://github.com/REditorSupport/vscode-R/issues/141)) +* Delete LICENSE +* Create LICENSE +* version 1.1.8 +* version 1.1.8 +* Delete LICENSE +* Send code all at once in bracketed paste mode +* Use no bracketed paste characters on Windows +* Fix bracketed paste on Windows ([#149](https://github.com/REditorSupport/vscode-R/issues/149)) +* Fix function call closing bracket highlight ([#151](https://github.com/REditorSupport/vscode-R/issues/151)) +* version 1.1.9 +* Hover works with update +* Attach active command switches session +* Detects changes to plot +* First implementation of showWebView +* Not change pid on webview response +* Use vscode.open to open plot file on update +* Markdown hover text +* Update view column +* Update webview options +* Use console logging +* start log watcher on activation +* Add status bar item +* Update status bar +* Add session init R script +* Add opt-in r.sessionWatcher option +* Implement deploySessionWatcher +* Read file in async method +* Refine updateSessionWatcher +* Remove unused data output init.R +* Add plot hook in session init.R to support ggplot2 +* Remove session files on terminal close +* Add rebind to init.R +* Add time stamp in respond +* Implement showDataView +* Force color in showWebView +* Update table class +* Refine table font size +* Include dataview resources +* Support View matrix +* Not rely on tempdir(check=TRUE) which requires R >= 3.5.0 +* Support browser command +* Add portMapping to WebView created in showBrowser +* Change name and title of browser WebView +* Change WebView title of browser +* Use workspaceFolders instead of deprecated rootPath +* Use WebView.asWebviewUri +* Use WebView.asWebviewUri +* Add R session watcher section to README.md +* Update README.md +* Use json for View(data.frame) +* Refine table_to_json in init.R +* Check windows in source script +* Check if init.R already sourced +* Use DataTables JS sourced data for View(data.frame) +* Refine showDataView +* remove outside files +* Use webpack to copy resources to dist +* Remove resources folder as no longer needed +* Update README.md +* Use column.type to fix ordering in View +* R session watcher ([#150](https://github.com/REditorSupport/vscode-R/issues/150)) +* version 1.2.0 +* Use empty order when creating DataTables in getTableHtml +* Use empty order when creating DataTables in getTableHtml ([#157](https://github.com/REditorSupport/vscode-R/issues/157)) +* Use utils::str in init.R ([#169](https://github.com/REditorSupport/vscode-R/issues/169)) +* Fix session watcher init.R path on Windows ([#177](https://github.com/REditorSupport/vscode-R/issues/177)) +* Support View(environment) +* Add initial support of View(function) +* Handle function in showDataView +* Support View any object +* Support View list that cannot be converted to json +* Make init.R more robust +* Make init.R more robust +* Refine init.R and use html help by default +* Use retainContextWhenHidden in all WebViews +* Update init.R +* Use FixedHeader extension in View(data.frame) +* Add row id to dataview_table for table without row names +* Extend View ([#161](https://github.com/REditorSupport/vscode-R/issues/161)) +* version 1.2.1 +* Update issue templates +* Provide completion for session symbols +* Update README.md +* Provide completion for elements in list-like objects +* Implement bracket completions +* Unify completion provider +* Use tryCatch in update +* Provide bracket completion with condition +* Provide completion for session symbols ([#165](https://github.com/REditorSupport/vscode-R/issues/165)) +* Initial implementation of plot history +* Make image viewer center of page +* Update plot history WebView and resources +* Add some error handling +* Show plot history ([#181](https://github.com/REditorSupport/vscode-R/issues/181)) +* Add row hover and select +* Use table-active as selected row style +* Add row hover and select ([#186](https://github.com/REditorSupport/vscode-R/issues/186)) +* Fix WebView Uri replacing ([#188](https://github.com/REditorSupport/vscode-R/issues/188)) +* Show page_viewer WebView in Active column +* Show WebView triggered by page_viewer in Active column ([#189](https://github.com/REditorSupport/vscode-R/issues/189)) +* Fix dataview_table handling single row data ([#198](https://github.com/REditorSupport/vscode-R/issues/198)) +* Use dev.args option when creating png device before replay +* Use dev.args option when creating png device before replay ([#182](https://github.com/REditorSupport/vscode-R/issues/182)) +* Update session watcher section in README.md +* Update README.md +* Update README.md +* Update README.md +* Add link and short description of radian +* Use R_PROFILE_USER +* init.R only work with TERM_PROGRAM=vscode +* Update README.md +* Respect existing R_PROFILE_USER +* Update README.md +* Update README.md +* Improve session watcher initialization ([#184](https://github.com/REditorSupport/vscode-R/issues/184)) + +### Refactor + +* refactor +* refactor and add webpack + +### Styling + +* style fix + +**Full Changelog**: + +## 0.6.1 - 2018-08-17 + +### Bug Fixes + +* Fix for #42 +* fix CO +* fix #65 ([#65](https://github.com/REditorSupport/vscode-R/issues/65)) +* fix tsconfig to publish +* fix dependencies +* fix categories +* fix dependency and lintr +* fix readability + +### Other + +* update dependency +* Fix for #42 ([#63](https://github.com/REditorSupport/vscode-R/issues/63)) +* revert fix +* version 0.5.8 +* version 0.5.9 +* remove lint function +* version 0.6.0 +* Issue 26: Added detection of bracket and pipe blocks. +* Issue 26: Added detection of bracket and pipe blocks. ([#82](https://github.com/REditorSupport/vscode-R/issues/82)) +* version 0.6.1 + +**Full Changelog**: + +## 0.5.7 - 2018-04-22 + +### Bug Fixes + +* fix grammer based on atom +* Fix for #61 + +### Other + +* update some dependencies +* version 0.5.6 +* disable lintr from default +* Additional Fix #61 ([#61](https://github.com/REditorSupport/vscode-R/issues/61)) +* version 0.5.7 + +### Refactor + +* refactor + +**Full Changelog**: + +## 0.5.5 - 2018-03-21 + +### Other + +* Add package dev commands +* Add package dev commands ([#58](https://github.com/REditorSupport/vscode-R/issues/58)) +* version 0.5.5 + +**Full Changelog**: + +## 0.5.4 - 2018-02-17 + +### Bug Fixes + +* fix +* fix lint +* fix Readme +* fix change log +* fix import order +* fix R syntax grammer +* fix light icon path +* Fix syntax +* fix by tslint +* fix lintr issue on windows +* fix document style +* fix default rterm.option +* fix default rterm +* fix snippets -* add setting - * `r.lintr.executable` #2 - * `r.rterm.option` #2 - * `r.source.encoding` (Thank you @ondrejpialek) #4 -* save before `R:Run Source` command #5 +### Other + +* Update README.md +* Update README.md +* Update README.md +* add shortcut +* version 0.4.0 +* source r short cut +* add SourcewithEcho +* version 0.4.2 +* init next function +* Added dataframe viewer +* Added sample data sets to demonstrate 5mb bug +* Mac and Linux hidden folder and disposal +* Cleaned up Preview Dataframe +* Cleaned package.json +* Restored package.json +* Added DS_Store to .gitignore +* Removed DS_Store +* Added Dataviewer Command ([#20](https://github.com/REditorSupport/vscode-R/issues/20)) +* version 0.4.3 +* lint fix +* add run source icon +* version 0.4.4 +* add document +* Create CODE_OF_CONDUCT.md +* Added data frame preview GIF +* Added Data frame GIF as img +* Corrected Markdown to Display GIF +* little fix +* update run icon(fix #21) ([#21](https://github.com/REditorSupport/vscode-R/issues/21)) +* remove extra test +* Fixed dataframe preview on win32; hidden folder and longer write wait +* Updated Dataframe Preview filesize limit +* remove extra dependencies +* Merge remote-tracking branch 'upstream/master' +* Update TODO +* version 0.4.5 +* Environment preview #23 +* version 0.4.5 +* remove extra files +* update typescript version +* update syntax +* mobr test files +* Update Readme and vesion up +* update some snippets from VS +* Attend win short cut +* version 0.4.8 +* Create ISSUE_TEMPLATE.md +* Slowed commands being pushed on RTerm +* Removed new line +* Proposed fix for Load Chunk problems #27 ([#31](https://github.com/REditorSupport/vscode-R/issues/31)) +* Added block detection and execute whole block +* Added warning if R client is not located. Corrected space in warning +* Added block detection and execute whole block ([#32](https://github.com/REditorSupport/vscode-R/issues/32)) +* add white space +* add shebang support for R syntax highlight ([#33](https://github.com/REditorSupport/vscode-R/issues/33)) * update snippets +* version 0.4.9 +* support lint package +* version 0.5.0 +* fix lintr issue on windows ([#35](https://github.com/REditorSupport/vscode-R/issues/35)) +* return lintr code +* support code region +* version 0.5.1 +* little fix +* version 0.5.2 +* version 0.5.3 +* R term name to R interactive (fix #46) ([#46](https://github.com/REditorSupport/vscode-R/issues/46)) +* Send code from Rmd chunk to terminal #49 +* version 0.5.4 + +### Refactor + +* refactor +* refactor +* refactor +* refactor +* refactor + +**Full Changelog**: + +## 0.3.9 - 2017-07-08 + +### Bug Fixes + +* fix +* fix +* fix lintr + +### Other + +* Added cursorMove down on line execution +* Don't pass Rterm comments +* Cleaned up skip comments +* Added cursorMove after line execution +* Update extension.ts +* Added cursorMove after line execution ([#13](https://github.com/REditorSupport/vscode-R/issues/13)) +* Don't pass Rterm comments ([#14](https://github.com/REditorSupport/vscode-R/issues/14)) +* version 0.3.8 +* update logo +* version 0.3.9 + +**Full Changelog**: + +## 0.3.7 - 2017-07-02 + +### Other + +* auto lintr +* version v0.3.7 + +**Full Changelog**: + +## 0.3.6 - 2017-06-23 + +### Bug Fixes + +* fix +* fix syntax +* fix #7 ([#7](https://github.com/REditorSupport/vscode-R/issues/7)) + +### Other + +* version 0.3.5 +* update +* little fix syntax +* version 0.3.6 + +### Refactor + +* refactor + +**Full Changelog**: + +## 0.3.4 - 2017-06-17 + +### Bug Fixes + +* fix something + +### Other + +* clean +* Merge pull request #1 from Ikuyadeu/master +* Fixed typos +* Fixed typos +* Fixed typos +* Fixing typos ([#12](https://github.com/REditorSupport/vscode-R/issues/12)) +* use rbox +* version 0.3.1 +* version 0.3.4 + +**Full Changelog**: + +## 0.3.1 - 2017-06-15 + +### Bug Fixes + +* fix #9 ([#9](https://github.com/REditorSupport/vscode-R/issues/9)) + +### Other + +* version 0.3.1 + +**Full Changelog**: + +## 0.3.0 - 2017-06-09 + +### Bug Fixes + +* fix lintr onMac +* fix lintr output + +### Other + +* version 0.2.9 +* update package +* version 0.3.0 + +**Full Changelog**: + +## 0.2.8 - 2017-06-04 + +### Other + +* add runSelection/Line +* version 0.2.8 + +**Full Changelog**: + +## 0.2.7 - 2017-06-04 + +### Other + +* update based project in README.md +* tslint +* set focus #5 +* version 0.2.7 + +**Full Changelog**: + +## 0.2.6 - 2017-06-02 + +### Bug Fixes + +* fix Terminal type +* fix #1 ([#1](https://github.com/REditorSupport/vscode-R/issues/1)) +* fix keywords +* fix for windows +* fix +* fix #2 ([#2](https://github.com/REditorSupport/vscode-R/issues/2)) + +### Other + +* update icon +* Create LICENCE +* add license +* version 0.1.8 +* add tslint +* setup use lintr +* support lintr +* version 0.2.0 +* add install lintr +* version 0.2.1 +* Delete vsc-extension-quickstart.md +* version 0.2.2 +* add option +* version 0.2.3 +* update README.md +* version 0.2.4 +* add selectedLine +* version 0.2.5 +* add keywords +* Add support for custom encoding (so that UTF-8 scripts can be executed properly) +* Custom encoding support ([#4](https://github.com/REditorSupport/vscode-R/issues/4)) +* add lintr term +* update r-snippets.json from atom language-r +* save before Run Source +* update README.md +* version 0.2.6 + +**Full Changelog**: + +## 0.1.3 - 2017-05-03 + +### Bug Fixes + +* fix perform -## v0.2.5 +### Other -* add `Run Selected` and `Run Source` command +* v0.1.3 -## v0.2.4 +**Full Changelog**: -* fix for Windows +## 0.1.2 - 2017-04-30 -## v0.2.3 +### Bug Fixes -* support lintr option cache and linters +* fix Readme.md -## v0.2.2 +### Other -* support lintr on Mac and Linux +* update summary in package and readme +* update package.json +* version 0.1.2 -## v0.2.0 +**Full Changelog**: -* support lintr on Windows +## 0.1.1 - 2017-04-29 -## v0.1.4 +### Bug Fixes -* use new icon +* fix change log +* fix run r perform +* fix for unix os -## v0.1.3 +### Other -* fix R term's perform +* version 0.0.9 +* update figure +* support r gitignore +* version 1.1 -## v0.1.2 +**Full Changelog**: -* fix packages +## 0.0.8 - 2017-04-11 -## v0.1.1 +### Other -* Create .gitignore +* remove rd-snippets +* add rmd snippets +* version 0.0.8 -## v0.0.9 +**Full Changelog**: -* Fix Run R perform +## 0.0.7 - 2017-04-09 -## v0.0.8 +### Bug Fixes -* R Markdown Snippets as Markdown +* fix readme +* fix readme -## v0.0.7 +### Other -* Support R Markdown +* update document +* update document +* support R Markdown +* version 0.0.7 -## v0.0.6 +**Full Changelog**: -* R Integrated Terminal +## 0.0.6 - 2017-04-07 -## v0.0.5 +### Other -* Rdocumentation Snippets +* make create R terminal +* integrated R +* version 0.0.6 -## v0.0.4 +**Full Changelog**: -* R Snippets +## 0.0.5 - 2017-04-05 -## v0.0.3 +### Bug Fixes -* Support R documentation +* fix Run .R +* fix test.r +* fix extension's name +* fix summary +* fix publisher -## v0.0.1 +### Other -* Initial release +* init +* add contributes +* update package.json +* add runR +* Update README.md +* createRterm only name +* set multi platform +* Tool to Tools +* update configuration +* add repository +* add feature.png +* Update README.md +* add icon +* update version +* support R doumantation +* version 0.0.3 +* add snippets +* version 0.0.4 +* support rd-snippets +* version 0.0.5 -## TODO +See [CHANGELOG.old.md](https://github.com/REditorSupport/vscode-R/blob/master/CHANGELOG.old.md) for changes before v2.8.5. -* Output Plot -* Debug -* Language Server -* Intellisense \ No newline at end of file + diff --git a/CHANGELOG.old.md b/CHANGELOG.old.md new file mode 100644 index 000000000..6513f5591 --- /dev/null +++ b/CHANGELOG.old.md @@ -0,0 +1,1011 @@ +# Change Log + +## Latest updates + +You can check all of our changes from [Release Page](https://github.com/REditorSupport/vscode-R/releases) + +## [2.8.5](https://github.com/REditorSupport/vscode-R/releases/tag/v2.8.5) + +* Upgrade Rsyntax +* Use --no-echo instead of --slave + +## [2.8.4](https://github.com/REditorSupport/vscode-R/releases/tag/v2.8.4) + +* Upgrade dependencies + +## [2.8.3](https://github.com/REditorSupport/vscode-R/releases/tag/v2.8.3) + +Enhancements: + +* Substitute variables in `r.rpath` and `r.rterm` settings. (#1444) +* Improve code chunk handling in base .R files. (#1454, thanks @kylebutts) + +Fixes: + +* Fix multiline smart-knit (#1493) +* Fix RMD Progress Bar (#1491) +* Remove `.` as an R language `editor.wordSeparators` (#1503, thanks @opasche) +* `numeric_version()` wants character as of R 4.4 (#1520, #1523, thanks @jennybc and @pawelru) +* Handle terminals created by vscode-Python (#1511, thanks @tomasnobrega) + +## [2.8.2](https://github.com/REditorSupport/vscode-R/releases/tag/v2.8.2) + +Enhancements: + +* Update built-in function match regex. (#1431, thanks @MichaelChirico) +* Add `r.useRenvLibPath` setting to opt in adding `renv` package cache to `.libPaths` when R processes (language server, help server, etc.) start up. (#1423, thanks @nateybear) +* Add a VScode task to run `testthat::test_file()`` on the currently open file. (#1415, thanks @gowerc) +* `r.rterm.*` settings now accept paths relative to the current workspace folder to support customized commands +to create R terminals. (#1398, thanks @Tal500) +* Upgrade ag-grid-community to v30.2.0 (#1434) +* Upgrade vscode-languageclient to v9.0.1 (#1435) + +## [2.8.1](https://github.com/REditorSupport/vscode-R/releases/tag/v2.8.1) + +Enhancements: + +* A new setting `r.lsp.multiServer` is added. If disabled, only a single language server will be spawned from the first workspace folder to handle all requests from all workspaces and files. (#1375) +* Upgrade ag-grid-community to v30.0.0 (#1379) + +Fixes: + +* Fix handling `r.session.data.pageSize = 0`. (#1364) +* Fix help panel in remote host. (#1374) +* Fix missing package names in "Install CRAN Package". (#1377) + +## [2.8.0](https://github.com/REditorSupport/vscode-R/releases/tag/v2.8.0) + +New Features: + +* A new experimental setting `r.session.useWebServer` is added to support communicating with R session via a web server running in R. This requires R package `httpuv` to be installed. Currently, +it enhances the session symbol completion when accessing R object via `$` and `@`. *This feature is +experimental and may be subject to change in the future.* (#1151) +* A new setting `r.rmarkdown.preview.zoom` is added to support the default zoom level or R markdown +preview. (#1333) + +Enhancements: + +* Improve message when error occurs on loading R packages. (#1334, thanks to @csaybar) +* Upgrade ag-grid-community to v29.3.0 (#1346) + +Fixes: + +* Commands that are not intended in the command pallete are now hidden. (#1327, #1330) + +## [2.7.2](https://github.com/REditorSupport/vscode-R/releases/tag/v2.7.2) + +Enhancements: + +* Upgrade vscode-languageclient to 8.1.0 (#1315) +* Workspace viewer will be cleaned-up when the attached R session exits. (#1318, #1321) +* A new command `r.view` is added to view selected objects. (#1319, thanks @yeyun1999) +* Workspace viewer commands that require an attached R session are now disabled when no R session is attached. (#1323) + +Fixes: + +* Workspace viewer now has a fallback message instead of causing error if session watcher is disabled. (#1317) + +## [2.7.1](https://github.com/REditorSupport/vscode-R/releases/tag/v2.7.1) + +New Features: + +* A new setting `r.source.echo` is added to support sending `source(file, echo = TRUE)` by default. (#1286, thanks @jakub-jedrusiak) +* A new setting `r.removeLeadingComments` is added to remove leading comments when sending code to terminal. (#1245, thanks @gowerc) + +Enhancements: + +* Help page previews from `.Rd` files are now generated asynchronously. (#1273) +* Column name is also displayed in the column tooltip in a data viewer. (#1278, thanks @eitsupi) +* Upgrade ag-grid-community to v29.0.0 (#1290) + +Fixes: + +* Fixed broken tests (#1302) + +## [2.7.0](https://github.com/REditorSupport/vscode-R/releases/tag/v2.7.0) + +New Features: + +* New syntax highlighting support for `NAMESPACE` and `.Rbuildignore`. (#1221, thanks @nx10) +* Support help preview in package development. (#1259, #1266) + +Enhancements: + +* The extension is re-published to [Open VSX Registry](https://open-vsx.org/extension/reditorsupport/r). ([open-vsx#591](https://github.com/open-vsx/publish-extensions/issues/591)). +* The WebView panel now supports htmlwidgets using Web Workers. (#1261, thanks @anthonynorth) +* Code block detection now includes parentheses, which is more consistent with RStudio behavior. (#1269) + +Fixes: + +* `View()` no longer stops with `tibble()` that contains objects that do not +implement `asJSON()` method. (#1255) +* Fixed the regex for detecting problems reported by testthat from tasks. (#1257, thans @gowerc) +* Fixed syntax highlighting in help preview under R 4.2.x. (#1268) + +## [2.6.1](https://github.com/REditorSupport/vscode-R/releases/tag/v2.6.1) + +Enhancements: + +* A new setting `r.plot.devArgs` is added to allow customizing png device arguments (e.g. width and height) for the PNG plot viewer. (#1235) + +Fixes: + +* Fixed opening requested file externally when viewer is disabled. (#1209) +* Support trailing slash in code-server's URI template. (#1241) + +## [2.6.0](https://github.com/REditorSupport/vscode-R/releases/tag/v2.6.0) + +New Features: + +* A new command "R: Generate C/C++ Configuration" is added to support auto-generating [`c_cpp_properties.json`](https://code.visualstudio.com/docs/cpp/customize-default-settings-cpp) in an R package with C/C++ code for [C/C++](https://marketplace.visualstudio.com/items?itemName=ms-vscode.cpptools) Extension to provide IntelliSense. (#1205, thanks @nx10) + +Enhancements: + +* Support showing KeTeX formula in help viewer. (#1213) + +Fixes: + +* Fixed empty line at the end of help pages as clickable example. (#1194) +* Avoid code highlighting in DESCRIPTION files in help viewer as code examples. (#1199) +* Saving a rmd file no longer triggers the preview to refresh if it is still rendering. (#1219) + +## [2.5.3](https://github.com/REditorSupport/vscode-R/releases/tag/v2.5.3) + +Enhancements: + +* Reload help pages on refresh. (#1188) +* Upgrade to vscode-languageclient 8.0.2. (#1173) + +Fixes: + +* Remove `encoding` from knitting so that renderers that do not have an encoding parameter (e.g. `quarto::quarto_render()`) now work properly. (#1167) + +## [2.5.2](https://github.com/REditorSupport/vscode-R/releases/tag/v2.5.2) + +New Features: + +* R help viewer now highlights code sections on hover and user can click the code to copy it to the clipboard, or press `ctrl+click` (Windows and Linux) or `cmd+click` (macOS) to send it to R terminal by default. A new setting `r.helpPanel.clickCodeExamples` is added to allow customizing the click behavior. (#1138) +* A new command `Create .lintr` is added. (#1112) + +Enhancements: + +* R and Rmd files are added to `Create: New File`. (#1119) +* Improved data viewer column resizing. (#1121) + +Fixes: + +* Hide environment values in R Markdown preview to prevent accidental deletion (#1117) +* Opening and closing a list item in the workspace viewer treeview now works properly. (#1150) + +## [2.5.0](https://github.com/REditorSupport/vscode-R/releases/tag/v2.5.0) + +Announcement: + +* [vscode-R](https://marketplace.visualstudio.com/items?itemName=REditorSupport.r) has been transferred to `REditorSupport` as the publisher in the VS Code Marketplace. The unique identifier has been updated to `REditorSupport.r`. (#690) +* [R in Visual Studio Code](https://code.visualstudio.com/docs/languages/r) topic is added to the VS Code documentation. + +New Features: + +* A new setting `r.libPaths` is added to support additional library paths to be appended to `.libPaths()` when R background processes (R language server and help server) are launched. It could be useful for projects with [renv](https://rstudio.github.io/renv/articles/renv.html) enabled where required packages (e.g. `languageserver` and `jsonlite`) to use vscode-R are only installed in other location. For more details, checkout the [wiki](https://github.com/REditorSupport/vscode-R/wiki/Working-with-renv-enabled-projects). (#1071, #1097, #1098) + +Enhancements: + +* The R package build task is separated into Build and Build Binary tasks. (#1029, thanks @Yunuuuu) +* Hide smart knit environment variables to prevent accidental deletion. (#1060) +* A new setting `r.session.data.pageSize` is added to support adjusting the page size of the data viewer. The default is now 500. (#1068) +* The check for languageserver package installation is improved and the prompt could be disabled. (#1071) +* The R Markdown code chunk snippet supports language choice. (#1082, thanks @jooyoungseo) +* It will prompt instead of showing empty choice when no R Markdown templates are found. (#1089) + +Fixes: + +* Guard against evaluation of active bindings in the global environment. (#1038) +* The `http` prefix is unnecessary and removed from several code snippets. (#1084, #1085, thanks @jooyoungseo) +* R Markdown knit and preview scripts now use `loadNamespace()` instead of `requireNamespace()` to fail early if necessary packages are unavailable. (#1086) + +## [2.4.0](https://github.com/REditorSupport/vscode-R/releases/tag/v2.4.0) + +New Features: + +* Added "R Markdown: New Draft" command to choose a template for a new R Markdown document. (#984) +* Added *Attached Namespaces* and *Loaded Namespaces* to the workspace viewer. (#1022) + +Enhancements: + +* `spawn` is consistently used to run R scripts and commands. (#985) +* Added a problemMatcher for testthat output from Test task. (#989, thanks @gowerc) +* Code chunk snippets now preserve selected text. (#1001) +* Added more useful Shiny and R Markdown snippets. (#1009, #1012, thanks @jooyoungseo). +* Provides optional `code` argument to `r.runSelection` command for other extensions to execute interactive R code. (#1017, thanks @jjallaire) +* Supports lambda function declaration in syntax higlighting. (#1025) + +Fixes: + +* Fixed code detection with mixed quotes. (#988, thanks @gowerc) +* Fixed syntax highlighting for variables starting with `function`. (#992, thanks @gowerc) +* Fixed R task definition and `resolveTask`. (#994) +* Fixed auto port forwarding for httpgd plot viewer in LiveShare session. (#1026) + +## [2.3.8](https://github.com/REditorSupport/vscode-R/releases/tag/v2.3.8) + +Fixes: + +* Fixes languageserver detection failure on Windows by avoiding rpath quoting. (#981) + +## [2.3.7](https://github.com/REditorSupport/vscode-R/releases/tag/v2.3.7) + +Note: + +* After v2.3.4, httpgd plot viewer requires `httpgd` 1.2.0 or later. If the plot viewer shows 404 error, installing the latest release of `httpgd` should resolve the problem. (#972) + +Enhancements: + +* Data viewer supports [Apache Arrow Table](https://arrow.apache.org/docs/r) and `r.session.data.rowLimit` setting is added to limit the number of rows to show. (#945, thanks @eitsupi) +* R gitignore file is updated and "R: Create gitignore" also supports multi-root workspace. (#949, thanks @eitsupi). +* Httpgd plot viewer has a delay before refreshing to avoid redrawing too often. (#956) +* Shell commands used in tasks use strong quoting. (#964, thanks @shrektan) +* User will be prompted to install `languageserver` if the package is missing. (#965, @shrektan) +* DCF syntax is updated to support syntax highlighting of `.lintr`. (#970, thanks @eitsupi) +* Column headers show the class and type of each column in tooltips. (#974, thanks @eitsupi) +* Extension is activated if the workspace folder contains `*.{rproj,Rproj,r,R,rd,Rd,rmd,Rmd}` at any level of sub-folders. (#979) + +Fixes: + +* Fix typo in command line arguments. (#954, thanks @achey2016) +* R Markdown commenting uses HTML-style comments outside code blocks. (#958) +* R Markdown rendering process gets `LANG` environment variable to properly handle unicode characters. (#961, thanks @shrektan) + +## [2.3.6](https://github.com/REditorSupport/vscode-R/releases/tag/v2.3.6) + +Enhancements: + +* Added raw string syntax. (#922) +* Added support for both single and double brackets in code-server's URI template. (#934, thanks @benz0li) + +Fixes: + +* Fixed syntax highlighting so that variables and function parameters are highlighted more consistently. (#939) +* R processes are now properly terminated on extension deactivation. (#941, thanks @albertosantini and @Yunuuuu) + +## [2.3.5](https://github.com/REditorSupport/vscode-R/releases/tag/v2.3.5) + +Enhancements: + +* Added `devtools` tasks to command palette. (#880, thanks @alex-gable) +* Improved help pages readability. (#915, thanks @18kimn) + +Fixes: + +* Fixed R Markdown knit and preview without opening a workspace folder. (#914) +* Fixed `DESCRIPTION` syntax highlighting for `Authors@R` field. (#920) +* Fixed an issue about leaking child processes. All spawned child processes (e.g. help server, language server, R Markdown preview) are cleaned up on exit. (#918) + +## [2.3.4](https://github.com/REditorSupport/vscode-R/releases/tag/v2.3.4) + +Enhancements: + +* Quotes in `r.rpath.*` settings are now removed. (#884) +* Alternative CRAN mirrors (e.g. [RStudio Public Package Manager](https://packagemanager.rstudio.com) and [the ropensci universe](https://ropensci.r-universe.dev) are supported. (#876) + +Fixes: + +* Fixed a Uri handling bug in Windows. (#888) +* Fixed a bug in restarting help server when library has changed. (#893) + +## [2.3.3](https://github.com/REditorSupport/vscode-R/releases/tag/v2.3.3) + +Enhancements: + +* The information of attached R session now appears in the label and the tooltip of +the status bar item. (#836) +* A new setting `r.rmarkdown.knit.command` is added to support customized knit command if not specified in the document. (#841, #850, thanks @xoolive) +* A terminal profile for R is added via the new terminal API. (#851) +* The help topics are now automatically updated when R packages are installed, removed, or upgraded. (#863) + +Fixes: + +* Fixed the problem with PowerShell on Windows when installing packages. (#846) +* Fixed the handling of single quote in roxygen comments and the roxygen block is now automatically exited after two empty lines. (#847) +* Backtick is added to the list of quote characters for syntax highlighting. (#859, thanks @jan-imbi) +* Fixed detecting the YAML frontmatter in R Markdown documents. (#856) +* Fixed attaching an R session with an open httpgd device that also triggers the plot viewer. (#852) +* Fixed the chunk coloring in R Markdown preview. (#867) +* Fixed the delimiter used in the output of the background knit process. (#868) + +## [2.3.2](https://github.com/REditorSupport/vscode-R/releases/tag/v2.3.2) + +Enhancements: + +* `.vsc.browser()` now handles `file://` urls. (#817) +* `r.session.levelOfObjectDetail` gains a `Normal` value for the session watcher to write only first level structure of global objects for performance. (#815) +* Session watcher now supports workspace folder as symlinks. (#827) + +Fixes: + +* Httpgd plot viewer respects the view column specified by `r.session.viewers.viewColumn.plot` setting (#816) +* `View` is completed replaced so that `tibble::view()` could +trigger data viewer (#818) +* Help cache is disabled between sessions (#819) + +## [2.3.1](https://github.com/REditorSupport/vscode-R/releases/tag/v2.3.1) + +Enhancements: + +* Proxied requests are now supported to work with [code-server](https://github.com/cdr/code-server). (#275, #803) + +Fixes: + +* `unsafe-eval` is re-enabled in WebView Content Security Policy to make htmlwidgets such as plotly work. (#805) +* The help viewer now respects `r.session.viewers.viewColumn.helpPanel`. (#804) +* The working directory of the knit background process is now consistent with the knit working directory so that `.Rprofile` and `renv` setup are respected. (#807) + +## [2.3.0](https://github.com/REditorSupport/vscode-R/releases/tag/v2.3.0) + +Enhancements + +* R Markdown preview now supports background rendering with progress bar, customizable + working directory, and smart knit button. (#765) +* `{rstudioapi}` emulation is enabled by default. (#769) +* A new setting `r.session.objectLengthLimit` is added to limit the output of the names of global objects with many named elements which might cause significant delay after inputs. (#778) +* `NA` and `Inf` could now be correctly displayed in the data viewer. (#780) +* User-specified R Markdown output format is now respected. (#785) + +Fixes + +* The security policy of WebView is relaxed to support `{flextable}` widgets. (#771) +* The R Markdown background rendering process could be properly terminated now. (#773) + +## [2.2.0](https://github.com/REditorSupport/vscode-R/releases/tag/v2.2.0) + +New Features + +* VS Code settings are now accessible from R and all vscode-specifc R options (`vsc.*`) now have +corresponding VS Code settings. (#743) + +Enhancements + +* Check conflict extension `mikhail-arkhipov.r` on activation. (#733) +* Add icons to WebViews. (#759) + +Fixes + +* Fix date filter in data viewer. (#736) +* Fix htmlwidget resource path in WebView. (#739) +* Use `.DollarNames` with default pattern. (#750) +* Fix syntax highlighting for `c()` in function args. (#751) +* Handle error in `capture_str()`. (#756) + +## [2.1.0](https://github.com/REditorSupport/vscode-R/releases/tag/v2.1.0) + +Important changes + +* The project is migrated to [REditorSupport](https://github.com/REditorSupport) organization on +GitHub. (#98) +* The R language service (completion, document outline, definition, etc., +formerly implemented in [vscode-r-lsp](https://github.com/REditorSupport/vscode-r-lsp)) is now +integrated into vscode-R (#695). The vscode-r-lsp extension will be unpublished from the +VS Code marketplace +at some point. + * Search `r-lsp` extension, uninstall it and vscode-R will start the R langauge service + automatically. + * The language service still depends on the R package [`languageserver`](https://github.com/REditorSupport/languageserver). Make sure the package is installed before using vscode-R. + * To opt-out the language service, set `"r.lsp.enabled": false` in your user settings. +* R session watcher is now enabled by default. (#670) + * `r.previewDataframe` and `r.previewEnvironment` will use the session watcher if enabled. + * To opt-out, set `"r.sessionWatcher": false` in your user settings. + +New Features + +* Preview R Markdown documents via background process with auto-refresh and dark theme support. (#692, #699) + +Enhancements + +* Several enhancements of the workspace viewer. (#672) +* The plot viewer now supports customizable CSS file via `r.plot.customStyleOverwrites` and + `r.plot.togglePreviewPlots` now cycles through mutlirow/scroll/hidden. (#678, #681) +* The data viewer is now based on [ag-grid](https://github.com/ag-grid/ag-grid) with better performance and better support for filtering and dark theme. (#708) + * The data viewer might not work with existing R sessions started before the extension update. + A restart of sessions is needed to use the new data viewer. +* Command `r.showPlotHistory` is removed in favor of the httpgd-based plot viewer. (#706) +* The plot viewer now supports full window mode. (#709) + +Fixes + +* LiveShare API bug fix and enhancements. (#679) +* Fix syntax highlighting of integers in scientific notation. (#683) + +## [2.0.0](https://github.com/REditorSupport/vscode-R/releases/tag/v2.0.0) + +Highlight + +* Thank you for join new collaborator: Elian H. Thiele-Evans(@ElianHugh) + * LiveShare Functionality #626 + * More detail about LiveShare: + * rmarkdown bug squashing and minor changes #663 + * Code cells in .R files #662 + +* Use .DollarNames for object with class in completion #660 + +## [1.6.7](https://github.com/REditorSupport/vscode-R/releases/tag/v1.6.7) + +* Update R syntax #647 +* Fix replacing base::.External.graphics #625 + +Thank you for your contributions. + +* @jolars + * Don't run chunks with eval = FALSE #653 (Fix #651) +* @nx10 + * Integrate httpgd #620 + +## [1.6.6](https://github.com/REditorSupport/vscode-R/releases/tag/v1.6.6) + +Highlight + +* Clarify error messages +* Being more conservative to call object.size() in task callback +* Send code to debug repl +* shim the rstudioapi if it has already been loaded + +Thank you for your contributions. + +* @krlmlr + * Update vscode engine #586 + * Satisfy markdownlint #587 +* @danielbasso + * Initial Workspace Viewer str() functionality #583 + +## 1.6.5 + +* Add links to help pages in hover #578 +* Move `r.runSource` and `r.knitRmd` to `editor/title/run` #573 (Fix #572) +* Fix so code can be run after creating terminal #567 +* Add option to keep terminal hidden after running code #566 +* Scroll to bottom after running a command #559 (Thank you @samkimhis) +* Refactoring and implementation of webviewPanelSerializer #556 +* add option vsc.hover.str.max.level #545 +* Change workspace tooltip #544 (Thank you @ElianHugh) + +## 1.6.4 + +* Better error message when reading aliases (#518) +* Keep promises and active bindings in globalenv (#521) +* Refactor extension.ts (#525) +* Write aliases to file (#526) +* add sendToConsole to rstudioapi emulation (#535) +* Add function to open help for selected text (#531) +* Add initial pipeline completion support (#530) +* Add updatePackage command (#532) +* Add option to preserve focus when opening help view (#541) + +## 1.6.3 + +* Add browser WebView command buttons #494 +* Enable find widget in WebViews #490 +* Disable alwaysShow for addin items #491 +* Only show R menu items in R view #493 +* Modify pre-release action #492 (Fix #484) +* Improve release action #505 (Fix #503) +* Improve help view #502 (Follow up to #497) + +### Thank you for contributors works 1.6.3 + +* @ElianHugh + * Implement R workspace viewer #476 (Fix #416) + * Conditionally show view #487 +* @tdeenes: Find in topic (help panel) #488 (Fix #463) +* @jsta: typo fix #500 + +## 1.6.2 + +* Improve style of help pages #481 + * All help pages: center headings + * Normal functino help pages: hide (rather useless) header bar + * All help pages: hide image placeholders + * Manuel Pages: hide page-internal links + * Manual Pages: suppress mismatching header styles embedded in the html +* Reorganize helppanel, add `?` function #477 +* Modify config #467 +* Fix bug that would leave background R processes running #475 + +* Fix whole of style (Extends #361) (#474) + +### Thank you for contributors works 1.6.2 + +* @markbaas: Fix The Ctrl+Enter shortcut does not work properly when a non-comment line in a function definition contains the "#" character. #462 (Fix #443) +* @kar9222: + * Update README #480 (Fix #465) + * RMarkdown: Add run & navigation commands. More customization. Refactor. #465 + +## 1.6.1 + +This version includes minor fix to stable new functions + +* Add GitHub Action for release #449 +* Highlight all chunks #453 +* Fix checking workspaceFolders in rHelpProviderOptions #456 +* Fix typo in help panel path config #457 + +* @kar9222 Thank you for contribution + * Update README: Add options(vsc.helpPanel = ...) #461 + * Rmd fenced block syntax highlighting for julia, python, etc #460 + +* New feature r.runFromLineToEnd #448 (Thank you @Dave-cruzz) + +## 1.6.0 + +* Integrate help view from vscode-R-help #433 (Implemented by the new collabolator @ManuelHentschel) +* Add terminal information to chooseTerminal error #447 +* Send code at EOF appends new line #444 +* Friendly error message when trying to launch addin picker and vsc.rstudioapi = FALSE #441 +* platform independent content string splitting #436 +* Add runAboveChunks command #434 + +## 1.5.2 + +* Enhance R markdown support #429 (Fix #428, #49, #261) +* Fix and enhance navigateToFile #430 +* Improve handling html help #427 (Fix #426, #380) + +## 1.5.1 + +* Rename init functions #425 (Fix #424) +* Fix issues in rstudioapi emulation #422 (Fix #421) + +## 1.5.0 + +* RStudio Addin Support #408 (Implemented by the new collabolator @MilesMcBain) + * The usage is added on the [wiki page](https://github.com/REditorSupport/vscode-R/wiki/RStudio-addin-support) + +* Recommend radian in README #420 + +## 1.4.6 + +* Remove Run in Active Terminal from README #413 (Fix #412) +* Remove command Run Selection/Line in Active Terminal #409 (Fix #306) +* Check url in browser #406 (Fix #371) + +## 1.4.5 + +* Remove shortcuts Ctrl + 1, 2, 3, 4, 5 #401 (Fix #368) + + These conflicted with default Visual Studio Code keyboard shortcuts. If you would like to restore them, see the [instructions in the Wiki](https://github.com/REditorSupport/vscode-R/wiki/Keyboard-shortcuts#removed-keyboard-shortcuts). + +* Restore R_PROFILE_USER #392 (Fix #391) +* Fix so rTerm is undefined when deleting terminal #403 (Fix #402) + +## 1.4.4 + +* Fix vulnerability issues + +## 1.4.2 + +* New R options and functions to control session watcher behavior #359 + + To work with existing self-managed, persistent R sessions as the extension is upgraded, + source the `init.R` again before attaching. + + ```r + source(file.path(Sys.getenv(if (.Platform$OS.type == "windows") "USERPROFILE" else "HOME"), ".vscode-R", "init.R")) + ``` + +* Remove single quote from doesLineEndInOperator #357 (Fix #356) + +## 1.4.1 + +* Fix View empty environment #350 (Fix #349) +* Change runSelectionInActiveTerm effect to warning #351 +* Improve getBrowserHtml #353 +* Use fs.watch instead of vscode.FileSystemWatcher #348 (Fix #347, #352, #236, #179, #272, #330) + +## 1.4.0 + +### Feature improvement + +* Add syntax highlight for DESCRIPTION and .Rproj #342 (Thank you @qinwf) +* A lot of works (Thank you @gowerc) + * Enable default R location to be used on mac/linux if none is supplied #340 + * Added functionality to switch to an existing R terminal #338 + * Expose send text delay as a parameter #336 + * Supress auto-opening quote in roxygen comment #328 +* Add r.runSelectionRetainCursor #325 + +### Project engineering + +* Convert language files to Json #333 (Thank you @gowerc) +* Define lint in package.json and use it in GitHub Actions #344 + +## 1.3.0 + +* Change so setting changes take effect immediately (Fix #301) +* Fix package volunerability +* Improve .Rprofile +* Remove --no-site-file from default r.rterm.option + +## 1.2.8 + +* Use eslint in GitHub Actions +* Add R Markdown surround and frontmatter comments (Fix #260) + +## 1.2.7 + +* Add [new wiki page](https://github.com/REditorSupport/vscode-R/wiki) ! +* Use Windows registry to find R path +* Fix handling grouped_df in dataview_table +* Use GitHub Actions for linting + +## 1.2.6 + +* Fix showWebView + +## 1.2.5 + +* Check untitled document and save result before running command + +## 1.2.4 + +* Add configurable command runner functions (Thank you @MilesMCBain) +* Change .Platform$GUI to vscode on session start +* Fixed the function snippet (Fixed #230) (Thank you @stanmart) +* Add statement of languageserver features to bug report template (Fixed #210) +* Inject R Markdown features into Markdown grammar (Fixed #220, #116, #48, #36) + +## 1.2.3 + +* Fixed the function snippet (Fixed #230) (Thank you @stanmart) +* Update activationEvents +* Add more logging to session watcher +* Avoid duplicate handling of response update +* Add syntax highlighting for R code in Rcpp comment #225 + +## 1.2.2 + +* View improvement (Thank you @renkun-ken) + * Fix dataview_table handling single row data + * Show WebView triggered by page_viewer in Active column + * Fix WebView Uri replacing + * Add row hover and select + * Improve session watcher initialization + * Use dev.args option when creating png device before replay + * Show plot history + +## 1.2.1 + +* Extend View (Thank you @renkun-ken) +* Fix session watcher init.R path on Windows (Fixed #176) + +## 1.2.0 + +* R session watcher (Thank you @renkun-ken). Usage is written on the README.md + * Attach Active Terminal (by command or clicking status bar item) + * Auto attach on R session startup: if init.R is sourced in .Rprofile, starting an R session will notify vscode-R to automatically attach to it. + * Provide hover to global symbol in attached session + * Show plot file on the fly + * Show WebView to present htmlwidgets and shiny apps + * Show WebView for data.frame and list object when calling View() + +## 1.1.9 + +* Fix bracketed paste on Windows (fix #117) +* Fix function call closing bracket highlight (Thank you @kiendang) + +## 1.1.8 + +* Use word under cursor for previewDataframe, nrow (fix #137) +* Change license MIT -> AGPL-3.0 + +## 1.1.6 + +* Fix behaviour when workplacefolders is Undefiend (Thank you @masterhands) +* Show r.term.option value in settings UI +* Refactoring + +## 1.1.5 + +* Replace deprecated function (Refactoring) +* Add alwaysUseActiveTerminal setting (fix #123) + +## 1.1.4 + +* Fixed spelling, improved formatting #129 (Thank you @wleoncio) +* Automatically comment new lines in roxygen sections (fix #124) +* Fix send code for newlines on Windows (fix #114) +* Add auto-completion of roxygen tags (fix #128) +* Change cursorMove to wrappedLineFirstNonWhitespaceCharacter (fix 126) + +## v1.1.3 + +* RMarkdown knit support (fix #121) (Thank you @dominicwhite) + +## v1.1.2 + +* Fix send code for newlines and Radian #114 #117 + +## v1.1.1 + +* Fix Preview Environment for variable x (fix #111) by @andycraig +* Fix Preview Environment for multi-class objects (fix #111) by @andycraig +* Fix danger package dependency + +## v1.1.0 + +* Fix for R markdown config +* Fix for valunerability + +## v1.0.9 + +* Fix check for Excel Viewer extension + +## v1.0.7 + +* Add web pack for performance by @andycraig + +## v1.0.6 + +* Add runSelectionInActiveTerm command #104 (fix #80 #102) (Thank you @andycraig) + +## v1.0.4 + +* Shortcuts with R functions #101 +(fix #100) (Thank you @MaTo04) + +## v1.0.3 + +* Fix Preview Dataframe command #67(fix #97) (Thank you @andycraig) + +## v1.0.2 + +* Remove excel dependency + +## v1.0.1 + +* Fix Dependency +* Refactoring + +## v1.0.0 + +* Sorry, supporting this extension is ended. Please looking forward to coming new one (). + +## v0.6.2 + +* fix wordPattern to avoid `.` +* fix run selection + +## v0.6.1 + +* Added detection of bracket and pipe blocks #82 (fix #26) (Thank you @andycraig) +* Fix dependency + +## v0.6.0 + +* Remove lintr function. If you want to use lintr, please install R LSP Client + +## v0.5.9 + +* Fix for security dependencies + +## v0.5.8 + +* Fix Run Selected has strange behavior #42 (Thank you @Ladvien) + +## v0.5.7 + +* Disabled lintr for default setting that is already implemented by LSP +* Fix Commented lines are not ignored when determining code blocks #61 (Thank you @Ladvien) + +## v0.5.6 + +* Fix some dependencies for perform and developments + +## v0.5.5 + +* Add package dev commands #58 (Thank you @jacob-long) + +## v0.5.4 + +* fix snippets +* R term name to R interactive (fix #46) +* Send code from Rmd chunk to terminal (fix #49) +* Depend R language server extension + +## v0.5.3 + +* fix default r.rterm.option again to `["--no-save", "--no-restore", "--no-site-file"]` + +## v0.5.2 + +* fix default r.rterm.option to `["--no-save", "--vanilla"]` + +## v0.5.1 + +* Support code region by `#region` and `#endregion` + +## v0.5.0 + +* Support package lint + +## v0.4.9 + +* Add shebang support for R syntax highlight #33(Thank you @dongzhuoer) +* Added block detection and execute whole block #32(Thank you @Ladvien) +* Proposed fix for Load Chunk problems #27 #31(Thank you @Ladvien) +* Update some snippets from VS + +## v0.4.8 + +* Fix Windows key map +* Add some snippets from VS + +## v0.4.7 + +* Fix syntax +* Fix Readme +* Fix icon + +## v0.4.6 + +* Added Environment Viewer command + +## v0.4.5 + +* Fix syntax little +* Set icon dark and light +* Improve data viewer perform(Thank you @Lavien) +* Remove extra package + +## v0.4.4 + +* Add `Run Source` icon + +## v0.4.3 + +* Added Data viewer Command(Thank you @Lavien) + +## v0.4.2 + +* Add Source with echo +* Fix keybind + +## v0.4.1 + +* Add more shortcut key + +## v0.4.0 + +* Add shortcut key +* Fix README.md + +## v0.3.9 + +* Fix problem lintr was running other language's files + +## v0.3.8 + +* Improve `Run Selection/Line` (Thank you @Ladvien) + * Added cursorMove after line execution #13 + * Don't pass Rterm comments #14 + +## v0.3.7 + +* run lintr on did save automaticaly + +## v0.3.6 + +* fix Terminal #7 + +## v0.3.5 + +* fix syntax + +## v0.3.4 + +* add "builtin function" from RBox + +## v0.3.3 + +* New syntax color from R Box +* fix typo(Thank you @Shians) #12 + +## v0.3.1 + +* fix Run Selection/Line only executes the first line of file when nothing was selected #9 + +## v0.3.0 + +* update lintr behavar + +## v0.2.9 + +* fix lintr on Mac + +## v0.2.8 + +* add command `R: Run Selection/Line` + +## v0.2.7 + +* add setting `r.source.focus` #5 + +## v0.2.6 + +* add setting + * `r.lintr.executable` #2 + * `r.rterm.option` #2 + * `r.source.encoding` (Thank you @ondrejpialek) #4 +* save before `R:Run Source` command #5 +* update snippets + +## v0.2.5 + +* add `Run Selected` and `Run Source` command + +## v0.2.4 + +* fix for Windows + +## v0.2.3 + +* support lintr option cache and linters + +## v0.2.2 + +* support lintr on Mac and Linux + +## v0.2.0 + +* support lintr on Windows + +## v0.1.4 + +* use new icon + +## v0.1.3 + +* fix R term's perform + +## v0.1.2 + +* fix packages + +## v0.1.1 + +* Create .gitignore + +## v0.0.9 + +* Fix Run R perform + +## v0.0.8 + +* R Markdown Snippets as Markdown + +## v0.0.7 + +* Support R Markdown + +## v0.0.6 + +* R Integrated Terminal + +## v0.0.5 + +* Rdocumentation Snippets + +## v0.0.4 + +* R Snippets + +## v0.0.3 + +* Support R documentation + +## v0.0.1 + +* Initial release + +## TODO + +* Output Plot +* Debug +* Language Server +* Intellisense diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md deleted file mode 100644 index 9dac91f63..000000000 --- a/CODE_OF_CONDUCT.md +++ /dev/null @@ -1,46 +0,0 @@ -# Contributor Covenant Code of Conduct - -## Our Pledge - -In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation. - -## Our Standards - -Examples of behavior that contributes to creating a positive environment include: - -* Using welcoming and inclusive language -* Being respectful of differing viewpoints and experiences -* Gracefully accepting constructive criticism -* Focusing on what is best for the community -* Showing empathy towards other community members - -Examples of unacceptable behavior by participants include: - -* The use of sexualized language or imagery and unwelcome sexual attention or advances -* Trolling, insulting/derogatory comments, and personal or political attacks -* Public or private harassment -* Publishing others' private information, such as a physical or electronic address, without explicit permission -* Other conduct which could reasonably be considered inappropriate in a professional setting - -## Our Responsibilities - -Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. - -Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. - -## Scope - -This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. - -## Enforcement - -Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at ikuyadeu0513@gmail.com. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. - -Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. - -## Attribution - -This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version] - -[homepage]: http://contributor-covenant.org -[version]: http://contributor-covenant.org/version/1/4/ diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index e25c252be..cc1233cd8 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,44 +1,3 @@ # Contributing to vscode-r -1. install npm and Node.js -1. install Visual Studio Code - -```sh -git clone https://github.com/Ikuyadeu/vscode-R.git -cd vscode-R -npm install -code . -``` - -## Test - -* Press `F5` if you open this project by VS Code - -## Pull Request - -* Fork it(via GitHub) in your account -* Add your project url - -```sh -git remote add mine https://github.com/yourname/vscode-R.git -``` - -* Create your feature branch - -```sh -git checkout -b my-new-feature -``` - -* Commit your changes - -```sh -git commit -am 'Add some feature' -``` - -* Push to the branch - -```sh -git push mine my-new-feature -``` - -* Create new Pull Request(via GitHub) \ No newline at end of file +If you are interested in writing code to fix issues, please see [How to Contribute](https://github.com/REditorSupport/vscode-R/wiki/Contributing) in the wiki. diff --git a/ISSUE_TEMPLATE.md b/ISSUE_TEMPLATE.md deleted file mode 100644 index eb9a7b417..000000000 --- a/ISSUE_TEMPLATE.md +++ /dev/null @@ -1,45 +0,0 @@ - -- VSCode Version: -- VSCode-R Version: -- OS Version: - -Steps to Reproduce: - -1. -1. - -Do you want to fix by self? (I hope your help!) - -Yes / No - -(If yes,) what kind of help do you want? (e.g. Which file should I fix, Survey (related documents) - - - -(If related)setting.json - -```json -// R.exe path for windows -"r.rterm.windows": "C:\\Program Files\\R\\R-3.4.4\\bin\\x64\\R.exe", - -// R path for Mac OS X -"r.rterm.mac": "/usr/local/bin/R", - -// R path for Linux -"r.rterm.linux": "/usr/bin/R", - -// R command line options (i.e: --vanilla) -"r.rterm.option": [], - -// An optional encoding to pass to R when executing the file, i.e. 'source(FILE, encoding=ENCODING)' -"r.source.encoding": "UTF-8", - -// Keeping focus when running -"r.source.focus": "editor", - -// Use active terminal for all commands, rather than creating a new R terminal -"r.alwaysUseActiveTerminal": false, - -// Use bracketed paste mode -"r.bracketedPaste": false, -``` diff --git a/LICENSE b/LICENSE index 9b14597d8..bd31956d6 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2017 Yuki Ueda +Copyright (c) 2025 REditorSupport Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/PULL_REQUEST_TEMPLATE.md b/PULL_REQUEST_TEMPLATE.md deleted file mode 100644 index 720a34c6a..000000000 --- a/PULL_REQUEST_TEMPLATE.md +++ /dev/null @@ -1,5 +0,0 @@ -**What problem did you solve?** - -**(If you have)Screenshot** - -**(If you do not have screenshot) How can I check this pull request?** diff --git a/R/.lintr b/R/.lintr new file mode 100644 index 000000000..c86c3fa1d --- /dev/null +++ b/R/.lintr @@ -0,0 +1,6 @@ +linters: linters_with_defaults( + line_length_linter(120), + indentation_linter(4), + return_linter = NULL, + object_name_linter = NULL, + object_usage_linter = NULL) diff --git a/R/cppProperties/extractLinkingTo.R b/R/cppProperties/extractLinkingTo.R new file mode 100644 index 000000000..ddffc65c3 --- /dev/null +++ b/R/cppProperties/extractLinkingTo.R @@ -0,0 +1,13 @@ +deps <- read.dcf("DESCRIPTION", "LinkingTo") +if (length(deps) == 0) { # Empty file + deps <- "" +} else { + deps <- unname(deps[1, ]) # Read 'LinkingTo' field from description + deps <- gsub("\\s|\\n|(\\([^\\)]*\\))", "", deps) # Remove all whitespace, line breaks and version constraints + deps <- strsplit(deps, ",")[[1]] # Split package names + deps <- vapply(deps, function(pkg) { + system.file("include", package = pkg) + }, character(1)) # Lookup include dir + deps <- utils::URLencode(deps) +} +writeLines(deps) diff --git a/R/help/getAliases.R b/R/help/getAliases.R new file mode 100644 index 000000000..8d0b27e20 --- /dev/null +++ b/R/help/getAliases.R @@ -0,0 +1,58 @@ +.paths <- .libPaths() + +add_lib_paths <- Sys.getenv("VSCR_LIB_PATHS") +if (nzchar(add_lib_paths)) { + add_lib_paths <- strsplit(add_lib_paths, "\n", fixed = TRUE)[[1L]] + .paths <- c(.paths, add_lib_paths) +} + +use_renv_lib_path <- Sys.getenv("VSCR_USE_RENV_LIB_PATH") +use_renv_lib_path <- if (nzchar(use_renv_lib_path)) as.logical(use_renv_lib_path) else FALSE +if (use_renv_lib_path) { + if (requireNamespace("renv", quietly = TRUE)) { + .paths <- c(.paths, renv::paths$cache()) + } else { + warning("renv package is not installed. Please install renv to use renv library path.") + } +} + +.libPaths(.paths) +message("R library paths: ", paste(.libPaths(), collapse = "\n")) + +loadNamespace("jsonlite") + +ip <- installed.packages() + +ord <- order(ip[, "Priority"]) +ip <- ip[ord, ] + +ret <- lapply(rownames(ip), function(row) { + libPath <- ip[row, "LibPath"] + pkg <- ip[row, "Package"] + filename <- file.path(libPath, pkg, "help", "aliases.rds") + info <- list( + package = pkg, + libPath = libPath, + aliasFile = filename, + aliases = NULL + ) + if (file.exists(filename)) { + res <- tryCatch( + expr = as.list(readRDS(filename)), + error = conditionMessage + ) + if (is.list(res)) { + info$aliases <- res + } else { + info$error <- res + } + } + info +}) + +names(ret) <- rownames(ip) + +lim <- Sys.getenv("VSCR_LIM") +json <- jsonlite::toJSON(ret, auto_unbox = TRUE) + +cat(lim, json, lim, sep = "\n", file = stdout()) diff --git a/R/help/helpServer.R b/R/help/helpServer.R new file mode 100644 index 000000000..e657965a9 --- /dev/null +++ b/R/help/helpServer.R @@ -0,0 +1,44 @@ +.paths <- .libPaths() + +add_lib_paths <- Sys.getenv("VSCR_LIB_PATHS") +if (nzchar(add_lib_paths)) { + add_lib_paths <- strsplit(add_lib_paths, "\n", fixed = TRUE)[[1L]] + .paths <- c(.paths, add_lib_paths) +} + +use_renv_lib_path <- Sys.getenv("VSCR_USE_RENV_LIB_PATH") +use_renv_lib_path <- if (nzchar(use_renv_lib_path)) as.logical(use_renv_lib_path) else FALSE +if (use_renv_lib_path) { + if (requireNamespace("renv", quietly = TRUE)) { + .paths <- c(.paths, renv::paths$cache()) + } else { + warning("renv package is not installed. Please install renv to use renv library path.") + } +} + +.libPaths(.paths) +message("R library paths: ", paste(.libPaths(), collapse = "\n")) + +lim <- Sys.getenv("VSCR_LIM") + +NEW_PACKAGE_STRING <- "NEW_PACKAGES" + +cat( + lim, + tools::startDynamicHelp(), + lim, + sep = "" +) + +currentPackages <- NULL + +while (TRUE) { + newPackages <- installed.packages(fields = "Packaged")[, c("Version", "Packaged")] + if (!identical(currentPackages, newPackages)) { + if (!is.null(currentPackages)) { + cat(NEW_PACKAGE_STRING, "\n") + } + currentPackages <- newPackages + } + Sys.sleep(1) +} diff --git a/R/help/rdToHtml.R b/R/help/rdToHtml.R new file mode 100644 index 000000000..e2523efad --- /dev/null +++ b/R/help/rdToHtml.R @@ -0,0 +1,21 @@ +#' Converts an .Rd file to HTML (output is printed to stdout) +#' +#' Execute this with the following trailing commandline args: +#' 1. path of an .Rd file +#' 2. name of the package +#' 3. version string of the package +#' 4. root dir of the package + +args <- base::commandArgs(TRUE) + +e <- tools::loadPkgRdMacros(args[4]) +e <- tools::loadRdMacros(file.path(R.home("share"), "Rd", "macros", "system.Rd"), macros = e) + +tools::Rd2HTML( + args[1], + package = args[2:3], + dynamic = TRUE, + encoding = "utf-8", + macros = e, + stages = c("build", "install", "render") +) diff --git a/R/languageServer.R b/R/languageServer.R new file mode 100644 index 000000000..4df0c3c05 --- /dev/null +++ b/R/languageServer.R @@ -0,0 +1,32 @@ +.paths <- .libPaths() + +add_lib_paths <- Sys.getenv("VSCR_LIB_PATHS") +if (nzchar(add_lib_paths)) { + add_lib_paths <- strsplit(add_lib_paths, "\n", fixed = TRUE)[[1L]] + .paths <- c(.paths, add_lib_paths) +} + +use_renv_lib_path <- Sys.getenv("VSCR_USE_RENV_LIB_PATH") +use_renv_lib_path <- if (nzchar(use_renv_lib_path)) as.logical(use_renv_lib_path) else FALSE +if (use_renv_lib_path) { + if (requireNamespace("renv", quietly = TRUE)) { + .paths <- c(.paths, renv::paths$cache()) + } else { + warning("renv package is not installed. Please install renv to use renv library path.") + } +} + +.libPaths(.paths) +message("R library paths: ", paste(.libPaths(), collapse = "\n")) + +if (!requireNamespace("languageserver", quietly = TRUE)) { + q(save = "no", status = 10) +} + +debug <- Sys.getenv("VSCR_LSP_DEBUG") +port <- Sys.getenv("VSCR_LSP_PORT") + +debug <- if (nzchar(debug)) as.logical(debug) else FALSE +port <- if (nzchar(port)) as.integer(port) else NULL + +languageserver::run(port = port, debug = debug) diff --git a/R/rmarkdown/knit.R b/R/rmarkdown/knit.R new file mode 100644 index 000000000..fc205141e --- /dev/null +++ b/R/rmarkdown/knit.R @@ -0,0 +1,34 @@ +# requires rmarkdown package to run (and knitr) +if (!requireNamespace("rmarkdown", quietly = TRUE)) { + stop("Knitting requires the {rmarkdown} package.") +} + +# get values from extension-set env values +# Hiding values is necessary to prevent their accidental removal +# See: https://github.com/REditorSupport/vscode-R/issues/860 +attach( + local({ + .vsc.knit_dir <- Sys.getenv("VSCR_KNIT_DIR") + .vsc.knit_lim <- Sys.getenv("VSCR_LIM") + .vsc.knit_command <- Sys.getenv("VSCR_KNIT_COMMAND") + environment() + }), + name = "tools:vscode", + warn.conflicts = FALSE +) + + +# set the knitr chunk eval directory +# mainly affects source calls +if (nzchar(.vsc.knit_dir)) { + knitr::opts_knit[["set"]](root.dir = .vsc.knit_dir) +} + +# render and get file output location for use in extension +cat( + .vsc.knit_lim, + eval(parse(text = .vsc.knit_command)), + .vsc.knit_lim, + sep = "", + file = stdout() +) diff --git a/R/rmarkdown/preview.R b/R/rmarkdown/preview.R new file mode 100644 index 000000000..b6bdd3f48 --- /dev/null +++ b/R/rmarkdown/preview.R @@ -0,0 +1,57 @@ +# requires rmarkdown package to run (and knitr) +if (!requireNamespace("rmarkdown", quietly = TRUE)) { + stop("Previewing documents requires the {rmarkdown} package.") +} + +# get values from extension-set env values +# Hiding values is necessary to prevent their accidental removal +# See: https://github.com/REditorSupport/vscode-R/issues/860 +attach( + local({ + .vsc.knit_dir <- Sys.getenv("VSCR_KNIT_DIR") + .vsc.knit_lim <- Sys.getenv("VSCR_LIM") + .vsc.file_path <- Sys.getenv("VSCR_FILE_PATH") + .vsc.output_file_loc <- Sys.getenv("VSCR_OUTPUT_FILE") + .vsc.tmp_dir <- Sys.getenv("VSCR_TMP_DIR") + + # if an output format ends up as html, we should not overwrite + # the format with rmarkdown::html_document() + .vsc.set_html <- tryCatch( + expr = { + lines <- suppressWarnings(readLines(.vsc.file_path, encoding = "UTF-8")) + out <- rmarkdown:::output_format_from_yaml_front_matter(lines) + output_format <- rmarkdown:::create_output_format(out$name, out$options) + if (!output_format$pandoc$to == "html") { + rmarkdown::html_document() + } else { + NULL + } + }, error = function(e) { + rmarkdown::html_document() + } + ) + environment() + }), + name = "tools:vscode", + warn.conflicts = FALSE +) + +# set the knitr chunk eval directory +# mainly affects source calls +if (nzchar(.vsc.knit_dir)) { + knitr::opts_knit[["set"]](root.dir = .vsc.knit_dir) +} + +# render and get file output location for use in extension +cat( + .vsc.knit_lim, + rmarkdown::render( + .vsc.file_path, + output_format = .vsc.set_html, + output_file = .vsc.output_file_loc, + intermediates_dir = .vsc.tmp_dir + ), + .vsc.knit_lim, + sep = "", + file = stdout() +) diff --git a/R/rmarkdown/templates.R b/R/rmarkdown/templates.R new file mode 100644 index 000000000..f226a6975 --- /dev/null +++ b/R/rmarkdown/templates.R @@ -0,0 +1,25 @@ +loadNamespace("jsonlite") +loadNamespace("yaml") + +pkgs <- .packages(all.available = TRUE) +templates <- new.env() +template_dirs <- lapply(pkgs, function(pkg) { + dir <- system.file("rmarkdown/templates", package = pkg) + if (dir.exists(dir)) { + ids <- list.dirs(dir, full.names = FALSE, recursive = FALSE) + for (id in ids) { + file <- file.path(dir, id, "template.yaml") + if (file.exists(file)) { + data <- yaml::read_yaml(file) + data$id <- id + data$package <- pkg + templates[[paste0(pkg, "::", id)]] <- data + } + } + } +}) + +template_list <- unname(as.list(templates)) +lim <- Sys.getenv("VSCR_LIM") +json <- jsonlite::toJSON(template_list, auto_unbox = TRUE) +cat(lim, json, lim, sep = "\n", file = stdout()) diff --git a/R/session/init.R b/R/session/init.R new file mode 100644 index 000000000..410263c2e --- /dev/null +++ b/R/session/init.R @@ -0,0 +1,104 @@ +# This file is executed with its containing directory as wd + +# Remember the working directory (should be extension subfolder that contains this script) +dir_init <- getwd() + + +# This function is run at the beginning of R's startup sequence +# Code that is meant to be run at the end of the startup should go in `init_last` +init_first <- function() { + # return early if not a vscode term session + if ( + !interactive() + || Sys.getenv("RSTUDIO") != "" + || Sys.getenv("TERM_PROGRAM") != "vscode" + ) { + return() + } + + # check required packages + required_packages <- c("jsonlite", "rlang") + missing_packages <- required_packages[ + !vapply(required_packages, requireNamespace, + logical(1L), quietly = TRUE + ) + ] + + if (length(missing_packages)) { + message( + "VSCode R Session Watcher requires ", + toString(missing_packages), ". ", + "Please install manually in order to use VSCode-R." + ) + } else { + # Initialize vsc utils after loading other default packages + assign(".First.sys", init_last, envir = globalenv()) + } +} + +old.First.sys <- .First.sys + +# Overwrite for `.First.sys` +# Is used to make sure that all default packages are loaded first +# Will be assigned to and called from the global environment, +# Will be run with wd being the user's working directory (!) +init_last <- function() { + old.First.sys() + + # cleanup previous version + removeTaskCallback("vscode-R") + options(vscodeR = NULL) + .vsc.name <- "tools:vscode" + if (.vsc.name %in% search()) { + detach(.vsc.name, character.only = TRUE) + } + + # Source vsc utils in new environmeent + .vsc <- new.env() + source(file.path(dir_init, "vsc.R"), local = .vsc) + + # attach functions that are meant to be called by the user/vscode + exports <- local({ + .vsc <- .vsc + .vsc.attach <- .vsc$attach + .vsc.view <- .vsc$show_dataview + .vsc.browser <- .vsc$show_browser + .vsc.viewer <- .vsc$show_viewer + .vsc.page_viewer <- .vsc$show_page_viewer + View <- .vsc.view + environment() + }) + attach(exports, name = .vsc.name, warn.conflicts = FALSE) + + # overwrite S3 bindings from other packages + suppressWarnings({ + if (!identical(getOption("vsc.helpPanel", "Two"), FALSE)) { + # Overwrite print function for results of `?` + .vsc$.S3method( + "print", + "help_files_with_topic", + .vsc$print.help_files_with_topic + ) + # Overwrite print function for results of `??` + .vsc$.S3method( + "print", + "hsearch", + .vsc$print.hsearch + ) + } + # Further S3 overwrites can go here + # ... + }) + + # remove this function from globalenv() + suppressWarnings( + rm(".First.sys", envir = globalenv()) + ) + + # Attach to vscode + exports$.vsc.attach() + + invisible() +} + +init_first() diff --git a/R/session/profile.R b/R/session/profile.R new file mode 100644 index 000000000..dafdf298b --- /dev/null +++ b/R/session/profile.R @@ -0,0 +1,33 @@ +# Source the original .Rprofile +local({ + try_source <- function(file) { + if (file.exists(file)) { + source(file) + TRUE + } else { + FALSE + } + } + + r_profile <- Sys.getenv("R_PROFILE_USER_OLD") + Sys.setenv( + R_PROFILE_USER_OLD = "", + R_PROFILE_USER = r_profile + ) + + if (nzchar(r_profile)) { + try_source(r_profile) + } else { + try_source(".Rprofile") || try_source(file.path("~", ".Rprofile")) + } + + invisible() +}) + +# Run vscode initializer +local({ + init_file <- Sys.getenv("VSCODE_INIT_R") + if (nzchar(init_file)) { + source(init_file, chdir = TRUE, local = TRUE) + } +}) diff --git a/R/session/rstudioapi.R b/R/session/rstudioapi.R new file mode 100644 index 000000000..60ea4358c --- /dev/null +++ b/R/session/rstudioapi.R @@ -0,0 +1,315 @@ +getActiveDocumentContext <- function() { + # In RStudio this returns either a document context for either the active + # source editor or active console. + # In VSCode this only ever returns the active (or last active) text editor. + # This is because it is currently not possible to tell in VSCode whether + # a text editor or terminal has focus. The concept of active is different. + # It means currently using or most recently used, and applies to text + # editors and terminals separately. + # This shoudln't be much of a limitation as the only context returned for + # the console was the current selection, so it is not very useful. + editor_context <- rstudioapi_call("active_editor_context") + + make_rs_document_context(editor_context) +} + +getSourceEditorContext <- getActiveDocumentContext + +verifyAvailable <- function(version_needed = NULL) { + if (is.null(version_needed)) TRUE else FALSE +} + +isAvailable <- function(version_needed = NULL, child_ok) { + verifyAvailable(version_needed) +} + +insertText <- function(location, text, id = NULL) { + + ## insertText also supports insertText("text"), insertText(text = "text"), + ## allowing the location parameter to be used for the text when + ## text itself is null. + ## This is dispatched as a separate request type + if (missing(text) && is.character(location) && length(location) == 1) { + ## handling insertText("text") + return(invisible( + rstudioapi_call("replace_text_in_current_selection", + text = location, + id = id + ) + )) + } else if (missing(location)) { + ## handling insertText(text = "text") + return(invisible(rstudioapi_call( + "replace_text_in_current_selection", + text = text, + id = id + ))) + } else if (is.null(location) && missing(text)) { + ## handling insertText(NULL) + return(invisible(NULL)) + } + + ## ensure normalised_location is a list containing a possible mix of + ## document_position and document_range objects + normalised_location <- normalise_pos_or_range_arg(location) + normalised_text <- normalise_text_arg(text, length(normalised_location)) + ## Having normalised we are guaranteed these are the same length. + ## Package up all the edits in a query to send to VSCode in an object + ## This is done so the edits can be applied in a single edit object, which + ## is hopefull closest to RStudio behaviour. + query <- + mapply(function(location, text) { + list( + operation = if (rstudioapi::is.document_range(location)) { + "modifyRange" + } else { + "insertText" + }, + location = location, + text = text + ) + }, + normalised_location, + normalised_text, + SIMPLIFY = FALSE + ) + + invisible( + rstudioapi_call("insert_or_modify_text", query = query, id = id) + ) +} + +modifyRange <- insertText + +readPreference <- function(name, default) { + ## in future we could map some rstudio preferences to vscode settings. + ## since the caller must provide a default this should work. + default +} + +readRStudioPreference <- readPreference + +.vsc_rstudioapi_env <- environment() + +hasFun <- function(name, version_needed = NULL, ...) { + if (!is.null(version_needed)) { + return(FALSE) + } + + obj <- .vsc_rstudioapi_env[[name]] + is.function(obj) && !identical(obj, .vsc_not_yet_implemented) +} + +findFun <- function(name, version_needed = NULL, ...) { + if (!is.null(version_needed)) { + stop("VSCode does not support used of 'version_needed'.") + } + + if (hasFun(name, version_needed = version_needed, ...)) { + .vsc_rstudioapi_env[[name]] + } else { + stop("Cannot find function '", name, "'") + } +} + +showDialog <- function(title, message, url = "") { + message <- sprintf("%s: %s \n%s", title, message, url) + invisible( + rstudioapi_call("show_dialog", message = message) + ) +} + +navigateToFile <- function(file, line = -1L, column = -1L) { + # normalise path since relative paths don't work as URIs in VSC + invisible( + rstudioapi_call( + "navigate_to_file", + file = normalizePath(file), + line = line, + column = column + ) + ) +} + +setSelectionRanges <- function(ranges, id = NULL) { + ranges_or_positions <- normalise_pos_or_range_arg(ranges) + + ranges <- lapply(ranges_or_positions, function(location) { + if (rstudioapi::is.document_position(location)) { + rstudioapi::document_range(location, location) + } else { + location + } + }) + + invisible( + rstudioapi_call("set_selection_ranges", ranges = ranges, id = id) + ) +} + +setCursorPosition <- setSelectionRanges + +documentSave <- function(id = NULL) { + invisible( + rstudioapi_call("document_save", id = id) + ) +} + +getActiveProject <- function() { + path_object <- rstudioapi_call("get_project_path") + if (is.null(path_object$path)) { + stop( + "No folder for active document. ", + "Is it unsaved? Try saving and run addin again." + ) + } + path_object$path +} + +.vsc_document_context <- function(id = NULL) { + doc_context <- rstudioapi_call("document_context", id = id) + doc_context +} + +documentId <- function(allowConsole = TRUE) document_context()$id$external + +documentPath <- function(id = NULL) document_context(id)$id$path + +documentSaveAll <- function() { + invisible( + rstudioapi_call("document_save_all") + ) +} + +documentNew <- function(text, + type = c("r", "rmarkdown", "sql"), + position = rstudioapi::document_position(0, 0), + execute = FALSE) { + if (!rstudioapi::is.document_position((position))) { + stop("DocumentNew requires a document_position object") + } + if (length(text) != 1 || !is.character(text)) { + stop("text for DocumentNew must be a length one character vector.") + } + if (execute) { + message( + "VSCode {rstudioapi} emulation does not support ", + " executing documents upon creation" + ) + } + + invisible( + rstudioapi_call( + "document_new", + text = text, + type = type, + position = position + ) + ) +} + +setDocumentContents <- function(text, id = NULL) { + whole_document_range <- + rstudioapi::document_range( + rstudioapi::document_position(0, 0), + rstudioapi::document_position(Inf, Inf) + ) + insertText(whole_document_range, text, id) +} + +restartSession <- function() { + invisible( + rstudioapi_call("restart_r") + ) +} + +viewer <- function(url, height = NULL) { + # cant bind to this directly because it's not created when the binding is + # made. + .vsc.viewer(url) +} + +getVersion <- function() { + numeric_version("0") +} + +versionInfo <- function() { + list( + citation = "", + mode = "vscode", + version = numeric_version("0"), + release_name = "vscode" + ) +} + +sendToConsole <- function(code, execute = TRUE, echo = TRUE, focus = FALSE) { + if (!echo) { + stop("rstudioapi::sendToConsole only supports echo = TRUE in VSCode.") + } + + code_to_run <- paste0(code, collapse = "\n") + invisible( + rstudioapi_call("send_to_console", code = code_to_run, execute = execute, focus = focus) + ) +} + + +# Unimplemented API calls that will error if called. + +.vsc_not_yet_implemented <- function(...) { + stop("This {rstudioapi} function is not currently implemented for VSCode.") +} + + +getConsoleEditorContext <- .vsc_not_yet_implemented +sourceMarkers <- .vsc_not_yet_implemented +documentClose <- .vsc_not_yet_implemented +showPrompt <- .vsc_not_yet_implemented +showQuestion <- .vsc_not_yet_implemented +updateDialog <- .vsc_not_yet_implemented +openProject <- .vsc_not_yet_implemented +initializeProject <- .vsc_not_yet_implemented +addTheme <- .vsc_not_yet_implemented +applyTheme <- .vsc_not_yet_implemented +convertTheme <- .vsc_not_yet_implemented +getThemeInfo <- .vsc_not_yet_implemented +getThemes <- .vsc_not_yet_implemented +removeTheme <- .vsc_not_yet_implemented +jobAdd <- .vsc_not_yet_implemented +jobAddOutput <- .vsc_not_yet_implemented +jobAddProgress <- .vsc_not_yet_implemented +jobRemove <- .vsc_not_yet_implemented +jobRunScript <- .vsc_not_yet_implemented +jobSetProgress <- .vsc_not_yet_implemented +jobSetState <- .vsc_not_yet_implemented +jobSetStatus <- .vsc_not_yet_implemented +launcherGetInfo <- .vsc_not_yet_implemented +launcherAvailable <- .vsc_not_yet_implemented +launcherGetJobs <- .vsc_not_yet_implemented +launcherConfig <- .vsc_not_yet_implemented +launcherContainer <- .vsc_not_yet_implemented +launcherControlJob <- .vsc_not_yet_implemented +launcherGetJob <- .vsc_not_yet_implemented +launcherHostMount <- .vsc_not_yet_implemented +launcherNfsMount <- .vsc_not_yet_implemented +launcherPlacementConstraint <- .vsc_not_yet_implemented +launcherResourceLimit <- .vsc_not_yet_implemented +launcherSubmitJob <- .vsc_not_yet_implemented +launcherSubmitR <- .vsc_not_yet_implemented +previewRd <- .vsc_not_yet_implemented +previewSql <- .vsc_not_yet_implemented +writePreference <- .vsc_not_yet_implemented +writeRStudioPreference <- .vsc_not_yet_implemented +getPersistentValue <- .vsc_not_yet_implemented +setPersistentValue <- .vsc_not_yet_implemented +savePlotAsImage <- .vsc_not_yet_implemented +createProjectTemplate <- .vsc_not_yet_implemented +hasColourConsole <- .vsc_not_yet_implemented +bugReport <- .vsc_not_yet_implemented +buildToolsCheck <- .vsc_not_yet_implemented +buildToolsInstall <- .vsc_not_yet_implemented +buildToolsExec <- .vsc_not_yet_implemented +dictionariesPath <- .vsc_not_yet_implemented +userDictionariesPath <- .vsc_not_yet_implemented +executeCommand <- .vsc_not_yet_implemented +translateLocalUrl <- .vsc_not_yet_implemented diff --git a/R/session/rstudioapi_util.R b/R/session/rstudioapi_util.R new file mode 100644 index 000000000..da5fe0ff6 --- /dev/null +++ b/R/session/rstudioapi_util.R @@ -0,0 +1,258 @@ +rstudioapi_call <- function(action, ...) { + request_response("rstudioapi", action = action, args = list(...)) +} + +rstudioapi_patch_hook <- function(api_env) { + patch_rstudioapi_fn <- + function(old, new) { + if (namespace_has(old, "rstudioapi")) { + assignInNamespace( + x = old, + value = new, + ns = "rstudioapi" + ) + } + } + ## make assignments to functions found in api_env namespace + ## that have function with the same name in {rstudioapi} namespace + api_list <- as.list(api_env) + mapply( + patch_rstudioapi_fn, + names(api_list), + api_list + ) +} + +make_rs_range <- function(vsc_selection) { + # vscode positions are zero indexed + # rstudioapi is one indexed + rstudioapi::document_range( + start = rstudioapi::document_position( + row = vsc_selection$start$line + 1, + column = vsc_selection$start$character + 1 + ), + end = rstudioapi::document_position( + row = vsc_selection$end$line + 1, + column = vsc_selection$end$character + 1 + ) + ) +} + +extract_document_ranges <- function(vsc_selections) { + lapply(vsc_selections, make_rs_range) +} + +to_content_lines <- function(contents, ranges) { + content_lines <- strsplit(contents, "\n|\r\n|\r$")[[1]] + + + # edge case handling: The cursor is at the start of a new empty line, + # and that line is the final line. + range_end_row <- unlist(lapply(ranges, function(range) range$end["row"])) + last_row <- max(range_end_row) + if (last_row == length(content_lines) + 1) { + content_lines <- c(content_lines, "") + } + + content_lines +} + + +extract_range_text <- function(range, content_lines) { + if (!range_has_text(range)) { + return("") + } + + content_rows <- + content_lines[(range$start["row"]):(range$end["row"])] + content_rows[length(content_rows)] <- + substring( + content_rows[length(content_rows)], + 1, + range$end["column"] - 1 + # it's a minus 1 here because the selection end point is the number + # of the first unselected column. I.e. range 1 - 2 is all of + # columns >= 1 and < 2, which is column 1. + ) + content_rows[1] <- + substring( + content_rows[1], + range$start["column"] + ) + + paste0(content_rows, collapse = "\n") +} + +range_has_text <- function(range) { + (range$end["row"] - range$start["row"]) + + (range$end["column"] - range$start["column"]) > 0 +} + +make_rs_document_selection <- function(ranges, range_texts) { + selection_data <- + mapply( + function(range, text) { + list( + range = range, + text = text + ) + }, + ranges, + range_texts, + SIMPLIFY = FALSE + ) + structure(selection_data, + class = "document_selection" + ) +} + +make_rs_document_context <- + function(vsc_editor_context) { + document_ranges <- + extract_document_ranges(vsc_editor_context$selection) + content_lines <- + to_content_lines(vsc_editor_context$contents, document_ranges) + document_range_texts <- + lapply( + document_ranges, + extract_range_text, + content_lines + ) + document_selection <- + make_rs_document_selection( + document_ranges, + document_range_texts + ) + + structure(list( + id = vsc_editor_context$id$external, + path = vsc_editor_context$path, + contents = content_lines, + selections = document_selection + ), + class = "document_context" + ) + } + +is_positionable <- function(p) is.numeric(p) && length(p) == 2 + +is_rangable <- function(r) is.numeric(r) && length(r) == 4 + +normalise_pos_or_range_arg <- function(location) { + # This is necessary due to the loose constraints of the location argument + # in rstudioapi::insertText and rstudioapi::modifyRange. These + # functions can take single vectors coerable to postition or range OR a + # list where each element may be a location, range, or a vector coercable + # to such. I prefer to normalise the argument to a list of either formal + # positions or ranges. + if (rstudioapi::is.document_position(location)) { + list(location) + } else if (is_positionable(location)) { + list(rstudioapi::as.document_position(location)) + } else if (rstudioapi::is.document_range(location)) { + list(location) + } else if (is_rangable(location)) { + list(rstudioapi::as.document_range(location)) + } else if (is.list(location)) { + lapply( + location, + function(a_location) { + if (rstudioapi::is.document_position(a_location) || rstudioapi::is.document_range(a_location)) { + a_location + } else if (is_positionable(a_location)) { + rstudioapi::as.document_position(a_location) + } else if (is_rangable((a_location))) { + rstudioapi::as.document_range(a_location) + } else { + stop( + "object in location list was not a", + " document_position or document_range" + ) + } + } + ) + } else { + stop("location object was not a document_position or document_range") + } +} + +normalise_text_arg <- function(text, location_length) { + if (length(text) == location_length) { + text + } else if (length(text) == 1 && location_length > 1) { + rep(text, location_length) + } else { + stop( + "text vector needs to be of length 1 or", + " the same length as location list" + ) + } +} + +update_addin_registry <- function(addin_registry) { + pkgs <- .packages(all.available = TRUE) + addin_files <- vapply(pkgs, function(pkg) { + system.file("rstudio/addins.dcf", package = pkg) + }, character(1L)) + addin_files <- addin_files[file.exists(addin_files)] + addin_descriptions <- + mapply( + function(package, package_dcf) { + addin_description_names <- + c( + "name", + "description", + "binding", + "interactive", + "package" + ) + description_result <- + tryCatch({ + addin_description <- + as.data.frame(read.dcf(package_dcf), + stringsAsFactors = FALSE + ) + + if (ncol(addin_description) < 4) { + NULL + } + ## if less than 4 columns it's malformed + ## a NULL will be ignored in the rbind + + addin_description$package <- package + names(addin_description) <- addin_description_names + + addin_description[, addin_description_names] + ## this filters out any extra columns + }, + error = function(cond) { + message( + "addins.dcf file for ", package, + " could not be read from R library. ", + "The RStudio addin picker will not ", + "contain it's addins" + ) + + NULL + } + ) + + description_result + }, + names(addin_files), + addin_files, + SIMPLIFY = FALSE + ) + addin_descriptions_flat <- + do.call( + function(...) rbind(..., make.row.names = FALSE), + addin_descriptions + ) + + jsonlite::write_json(addin_descriptions_flat, addin_registry, pretty = TRUE) +} + +namespace_has <- function(obj, namespace) { + attempt <- try(getFromNamespace(obj, namespace), silent = TRUE) + !inherits(attempt, "try-error") +} diff --git a/R/session/vsc.R b/R/session/vsc.R new file mode 100644 index 000000000..5ab461394 --- /dev/null +++ b/R/session/vsc.R @@ -0,0 +1,941 @@ +pid <- Sys.getpid() +wd <- getwd() +tempdir <- tempdir() +homedir <- Sys.getenv( + if (.Platform$OS.type == "windows") "USERPROFILE" else "HOME" +) +dir_watcher <- Sys.getenv("VSCODE_WATCHER_DIR", file.path(homedir, ".vscode-R")) +request_file <- file.path(dir_watcher, "request.log") +request_lock_file <- file.path(dir_watcher, "request.lock") +settings_file <- file.path(dir_watcher, "settings.json") +user_options <- names(options()) + +logger <- if (getOption("vsc.debug", FALSE)) { + function(...) cat(..., "\n", sep = "") +} else { + function(...) invisible() +} + +load_settings <- function() { + if (!file.exists(settings_file)) { + return(FALSE) + } + + setting <- function(x, ...) { + switch(EXPR = x, ..., x) + } + + mapping <- quote(list( + vsc.use_webserver = session$useWebServer, + vsc.use_httpgd = plot$useHttpgd, + vsc.show_object_size = workspaceViewer$showObjectSize, + vsc.rstudioapi = session$emulateRStudioAPI, + vsc.str.max.level = setting(session$levelOfObjectDetail, Minimal = 0, Normal = 1, Detailed = 2), + vsc.object_length_limit = session$objectLengthLimit, + vsc.object_timeout = session$objectTimeout, + vsc.globalenv = session$watchGlobalEnvironment, + vsc.plot = setting(session$viewers$viewColumn$plot, Disable = FALSE), + vsc.dev.args = plot$devArgs, + vsc.browser = setting(session$viewers$viewColumn$browser, Disable = FALSE), + vsc.viewer = setting(session$viewers$viewColumn$viewer, Disable = FALSE), + vsc.page_viewer = setting(session$viewers$viewColumn$pageViewer, Disable = FALSE), + vsc.row_limit = session$data$rowLimit, + vsc.view = setting(session$viewers$viewColumn$view, Disable = FALSE), + vsc.helpPanel = setting(session$viewers$viewColumn$helpPanel, Disable = FALSE) + )) + + vsc_settings <- tryCatch(jsonlite::read_json(settings_file), error = function(e) { + message("Error occurs when reading VS Code settings: ", conditionMessage(e)) + }) + + if (is.null(vsc_settings)) { + return(FALSE) + } + + ops <- eval(mapping, vsc_settings) + + # exclude options set by user on startup + r_options <- ops[!(names(ops) %in% user_options)] + + options(r_options) +} + +load_settings() + +if (is.null(getOption("help_type"))) { + options(help_type = "html") +} + +use_webserver <- isTRUE(getOption("vsc.use_webserver", FALSE)) +if (use_webserver) { + if (requireNamespace("httpuv", quietly = TRUE)) { + request_handlers <- list( + hover = function(expr, ...) { + tryCatch({ + expr <- parse(text = expr, keep.source = FALSE)[[1]] + obj <- eval(expr, .GlobalEnv) + list(str = capture_str(obj)) + }, error = function(e) NULL) + }, + + complete = function(expr, trigger, ...) { + obj <- tryCatch({ + expr <- parse(text = expr, keep.source = FALSE)[[1]] + eval(expr, .GlobalEnv) + }, error = function(e) NULL) + + if (is.null(obj)) { + return(NULL) + } + + if (trigger == "$") { + names <- if (is.object(obj)) { + .DollarNames(obj, pattern = "") + } else if (is.recursive(obj)) { + names(obj) + } else { + NULL + } + + result <- lapply(names, function(name) { + item <- obj[[name]] + list( + name = name, + type = typeof(item), + str = try_capture_str(item) + ) + }) + return(result) + } + + if (trigger == "@" && isS4(obj)) { + names <- slotNames(obj) + result <- lapply(names, function(name) { + item <- slot(obj, name) + list( + name = name, + type = typeof(item), + str = try_capture_str(item) + ) + }) + return(result) + } + } + ) + + server <- getOption("vsc.server") + if (!is.null(server) && server$isRunning()) { + host <- server$getHost() + port <- server$getPort() + token <- attr(server, "token") + } else { + host <- "127.0.0.1" + port <- httpuv::randomPort() + token <- sprintf("%d:%d:%.6f", pid, port, Sys.time()) + server <- httpuv::startServer(host, port, + list( + onHeaders = function(req) { + logger("http request ", + req[["REMOTE_ADDR"]], ":", + req[["REMOTE_PORT"]], " ", + req[["REQUEST_METHOD"]], " ", + req[["HTTP_USER_AGENT"]] + ) + + if (!nzchar(req[["REMOTE_ADDR"]]) || identical(req[["REMOTE_PORT"]], "0")) { + return(NULL) + } + + if (!identical(req[["HTTP_AUTHORIZATION"]], token)) { + return(list( + status = 401L, + headers = list( + "Content-Type" = "text/plain" + ), + body = "Unauthorized" + )) + } + + if (!identical(req[["HTTP_CONTENT_TYPE"]], "application/json")) { + return(list( + status = 400L, + headers = list( + "Content-Type" = "text/plain" + ), + body = "Bad request" + )) + } + }, + call = function(req) { + content <- req$rook.input$read_lines() + request <- jsonlite::fromJSON(content, simplifyVector = FALSE) + handler <- request_handlers[[request$type]] + response <- if (is.function(handler)) do.call(handler, request) + + list( + status = 200L, + headers = list( + "Content-Type" = "application/json" + ), + body = jsonlite::toJSON(response, auto_unbox = TRUE, force = TRUE) + ) + } + ) + ) + attr(server, "token") <- token + options(vsc.server = server) + } + } else { + message("{httpuv} is required to use WebServer from the session watcher.") + use_webserver <- FALSE + } +} + +get_timestamp <- function() { + sprintf("%.6f", Sys.time()) +} + +scalar <- function(x) { + class(x) <- c("scalar", class(x)) + x +} + +request <- function(command, ...) { + obj <- list( + time = Sys.time(), + pid = pid, + wd = wd, + command = command, + ... + ) + jsonlite::write_json(obj, request_file, + auto_unbox = TRUE, null = "null", force = TRUE + ) + cat(get_timestamp(), file = request_lock_file) +} + +try_catch_timeout <- function(expr, timeout = Inf, ...) { + expr <- substitute(expr) + envir <- parent.frame() + setTimeLimit(timeout, transient = TRUE) + on.exit(setTimeLimit()) + tryCatch(eval(expr, envir), ...) +} + +capture_str <- function(object, max.level = getOption("vsc.str.max.level", 0)) { + paste0(utils::capture.output( + utils::str(object, + max.level = max.level, + give.attr = FALSE, + vec.len = 1 + ) + ), collapse = "\n") +} + +try_capture_str <- function(object, max.level = getOption("vsc.str.max.level", 0)) { + tryCatch( + capture_str(object, max.level = max.level), + error = function(e) { + paste0(class(object), collapse = ", ") + } + ) +} + +rebind <- function(sym, value, ns) { + if (is.character(ns)) { + Recall(sym, value, getNamespace(ns)) + pkg <- paste0("package:", ns) + if (pkg %in% search()) { + Recall(sym, value, as.environment(pkg)) + } + } else if (is.environment(ns)) { + if (bindingIsLocked(sym, ns)) { + unlockBinding(sym, ns) + on.exit(lockBinding(sym, ns)) + } + assign(sym, value, ns) + } else { + stop("ns must be a string or environment") + } +} + +address <- function(x) { + info <- utils::capture.output(.Internal(inspect(x, 0L, 0L))) + sub("@([a-z0-9]+)\\s+.+", "\\1", info[[1]]) +} + +globalenv_cache <- new.env(parent = emptyenv()) + +inspect_env <- function(env, cache) { + all_names <- ls(env) + rm(list = setdiff(names(globalenv_cache), all_names), envir = cache) + is_active <- vapply(all_names, bindingIsActive, logical(1), USE.NAMES = TRUE, env) + is_promise <- rlang::env_binding_are_lazy(env, all_names[!is_active]) + show_object_size <- getOption("vsc.show_object_size", FALSE) + object_length_limit <- getOption("vsc.object_length_limit", 2000) + object_timeout <- getOption("vsc.object_timeout", 50) / 1000 + str_max_level <- getOption("vsc.str.max.level", 0) + objs <- lapply(all_names, function(name) { + if (isTRUE(is_promise[name])) { + info <- list( + class = "promise", + type = scalar("promise"), + length = scalar(0L), + str = scalar("(promise)") + ) + } else if (isTRUE(is_active[name])) { + info <- list( + class = "active_binding", + type = scalar("active_binding"), + length = scalar(0L), + str = scalar("(active-binding)") + ) + } else { + obj <- env[[name]] + + info <- list( + class = class(obj), + type = scalar(typeof(obj)), + length = scalar(length(obj)) + ) + + if (show_object_size) { + addr <- address(obj) + cobj <- cache[[name]] + if (is.null(cobj) || cobj$address != addr || cobj$length != info$length) { + cache[[name]] <- cobj <- list( + address = addr, + length = length(obj), + size = unclass(object.size(obj)) + ) + } + info$size <- scalar(cobj$size) + } + + if (length(obj) > object_length_limit) { + info$str <- scalar(trimws(try_capture_str(obj, 0))) + } else { + info_str <- NULL + if (str_max_level > 0) { + info_str <- try_catch_timeout( + capture_str(obj, str_max_level), + timeout = object_timeout, + error = function(e) NULL + ) + } + if (is.null(info_str)) { + info_str <- try_capture_str(obj, 0) + } + info$str <- scalar(trimws(info_str)) + obj_names <- if (is.object(obj)) { + .DollarNames(obj, pattern = "") + } else if (is.recursive(obj)) { + names(obj) + } else { + NULL + } + + if (length(obj_names)) { + info$names <- obj_names + } + } + + if (isS4(obj)) { + info$slots <- slotNames(obj) + } + + if (!is.null(dim(obj))) { + info$dim <- dim(obj) + } + } + info + }) + names(objs) <- all_names + objs +} + +dir_session <- file.path(tempdir, "vscode-R") +dir.create(dir_session, showWarnings = FALSE, recursive = TRUE) + +removeTaskCallback("vsc.workspace") +show_globalenv <- isTRUE(getOption("vsc.globalenv", TRUE)) +workspace_file <- file.path(dir_session, "workspace.json") +workspace_lock_file <- file.path(dir_session, "workspace.lock") +file.create(workspace_lock_file, showWarnings = FALSE) + +update_workspace <- function(...) { + tryCatch({ + data <- list( + search = search()[-1], + loaded_namespaces = loadedNamespaces(), + globalenv = if (show_globalenv) inspect_env(.GlobalEnv, globalenv_cache) else NULL + ) + jsonlite::write_json(data, workspace_file, force = TRUE, pretty = FALSE) + cat(get_timestamp(), file = workspace_lock_file) + }, error = message) + TRUE +} +update_workspace() +addTaskCallback(update_workspace, name = "vsc.workspace") + +removeTaskCallback("vsc.plot") +use_httpgd <- identical(getOption("vsc.use_httpgd", FALSE), TRUE) +show_plot <- !identical(getOption("vsc.plot", "Two"), FALSE) +if (use_httpgd && "httpgd" %in% .packages(all.available = TRUE)) { + options(device = function(...) { + httpgd::hgd( + silent = TRUE + ) + .vsc$request("httpgd", url = httpgd::hgd_url()) + }) +} else if (use_httpgd) { + message("Install package `httpgd` to use vscode-R with httpgd!") +} else if (show_plot) { + plot_file <- file.path(dir_session, "plot.png") + plot_lock_file <- file.path(dir_session, "plot.lock") + file.create(plot_file, plot_lock_file, showWarnings = FALSE) + + plot_updated <- FALSE + null_dev_id <- c(pdf = 2L) + null_dev_size <- c(7 + pi, 7 + pi) + + check_null_dev <- function() { + identical(dev.cur(), null_dev_id) && + identical(dev.size(), null_dev_size) + } + + new_plot <- function() { + if (check_null_dev()) { + plot_updated <<- TRUE + } + } + + options( + device = function(...) { + pdf(NULL, + width = null_dev_size[[1L]], + height = null_dev_size[[2L]], + bg = "white") + dev.control(displaylist = "enable") + } + ) + + update_plot <- function(...) { + tryCatch({ + if (plot_updated && check_null_dev()) { + plot_updated <<- FALSE + record <- recordPlot() + if (length(record[[1L]])) { + dev_args <- getOption("vsc.dev.args") + do.call(png, c(list(filename = plot_file), dev_args)) + on.exit({ + dev.off() + cat(get_timestamp(), file = plot_lock_file) + }) + replayPlot(record) + } + } + }, error = message) + TRUE + } + + setHook("plot.new", new_plot, "replace") + setHook("grid.newpage", new_plot, "replace") + + rebind(".External.graphics", function(...) { + out <- .Primitive(".External.graphics")(...) + if (check_null_dev()) { + plot_updated <<- TRUE + } + out + }, "base") + + update_plot() + addTaskCallback(update_plot, name = "vsc.plot") +} + +show_view <- !identical(getOption("vsc.view", "Two"), FALSE) +if (show_view) { + get_column_def <- function(name, field, value) { + filter <- TRUE + tooltip <- sprintf( + "%s, class: [%s], type: %s", + name, + toString(class(value)), + typeof(value) + ) + if (is.numeric(value)) { + type <- "numericColumn" + if (is.null(attr(value, "class"))) { + filter <- "agNumberColumnFilter" + } + } else if (inherits(value, "Date")) { + type <- "dateColumn" + filter <- "agDateColumnFilter" + } else { + type <- "textColumn" + filter <- "agTextColumnFilter" + } + list( + headerName = name, + headerTooltip = tooltip, + field = field, + type = type, + filter = filter + ) + } + + dataview_table <- function(data) { + if (is.matrix(data)) { + data <- as.data.frame.matrix(data) + } + + if (is.data.frame(data)) { + .nrow <- nrow(data) + .colnames <- colnames(data) + if (is.null(.colnames)) { + .colnames <- sprintf("V%d", seq_len(ncol(data))) + } else { + .colnames <- trimws(.colnames) + } + if (.row_names_info(data) > 0L) { + rownames <- rownames(data) + rownames(data) <- NULL + } else { + rownames <- seq_len(.nrow) + } + .colnames <- c("(row)", .colnames) + fields <- sprintf("x%d", seq_along(.colnames)) + data <- c(list(" " = rownames), .subset(data)) + names(data) <- fields + class(data) <- "data.frame" + attr(data, "row.names") <- .set_row_names(.nrow) + columns <- .mapply(get_column_def, + list(.colnames, fields, data), + NULL + ) + list( + columns = columns, + data = data + ) + } else { + stop("data must be a data.frame or a matrix") + } + } + + show_dataview <- function(x, title, uuid = NULL, + viewer = getOption("vsc.view", "Two"), + row_limit = abs(getOption("vsc.row_limit", 0))) { + as_truncated_data <- function(.data) { + .nrow <- nrow(.data) + if (row_limit != 0 && row_limit < .nrow) { + title <<- sprintf("%s (limited to %d/%d)", title, row_limit, .nrow) + .data <- utils::head(.data, n = row_limit) + } + return(.data) + } + + if (missing(title)) { + sub <- substitute(x) + title <- deparse(sub, nlines = 1) + } + if (inherits(x, "ArrowTabular")) { + x <- as_truncated_data(x) + x <- as.data.frame(x) + } + if (is.environment(x)) { + all_names <- ls(x) + is_active <- vapply(all_names, bindingIsActive, logical(1), USE.NAMES = TRUE, x) + is_promise <- rlang::env_binding_are_lazy(x, all_names[!is_active]) + x <- lapply(all_names, function(name) { + if (isTRUE(is_promise[name])) { + data.frame( + class = "promise", + type = "promise", + length = 0L, + size = 0L, + value = "(promise)", + stringsAsFactors = FALSE, + check.names = FALSE + ) + } else if (isTRUE(is_active[name])) { + data.frame( + class = "active_binding", + type = "active_binding", + length = 0L, + size = 0L, + value = "(active-binding)", + stringsAsFactors = FALSE, + check.names = FALSE + ) + } else { + obj <- x[[name]] + data.frame( + class = paste0(class(obj), collapse = ", "), + type = typeof(obj), + length = length(obj), + size = as.integer(object.size(obj)), + value = trimws(try_capture_str(obj, 0)), + stringsAsFactors = FALSE, + check.names = FALSE + ) + } + }) + names(x) <- all_names + if (length(x)) { + x <- do.call(rbind, x) + } else { + x <- data.frame( + class = character(), + type = character(), + length = integer(), + size = integer(), + value = character(), + stringsAsFactors = FALSE, + check.names = FALSE + ) + } + } + if (is.data.frame(x) || is.matrix(x)) { + x <- as_truncated_data(x) + data <- dataview_table(x) + file <- tempfile(tmpdir = tempdir, fileext = ".json") + jsonlite::write_json(data, file, na = "string", null = "null", auto_unbox = TRUE, force = TRUE) + request("dataview", source = "table", type = "json", + title = title, file = file, viewer = viewer, uuid = uuid + ) + } else if (is.list(x)) { + tryCatch({ + file <- tempfile(tmpdir = tempdir, fileext = ".json") + jsonlite::write_json(x, file, na = "string", null = "null", auto_unbox = TRUE, force = TRUE) + request("dataview", source = "list", type = "json", + title = title, file = file, viewer = viewer, uuid = uuid + ) + }, error = function(e) { + file <- file.path(tempdir, paste0(make.names(title), ".txt")) + text <- utils::capture.output(print(x)) + writeLines(text, file) + request("dataview", source = "object", type = "txt", + title = title, file = file, viewer = viewer, uuid = uuid + ) + }) + } else { + file <- file.path(tempdir, paste0(make.names(title), ".R")) + if (is.primitive(x)) { + code <- utils::capture.output(print(x)) + } else { + code <- deparse(x) + } + writeLines(code, file) + request("dataview", source = "object", type = "R", + title = title, file = file, viewer = viewer, uuid = uuid + ) + } + } + + rebind("View", show_dataview, "utils") +} + +attach <- function() { + load_settings() + if (rstudioapi_enabled()) { + rstudioapi_util_env$update_addin_registry(addin_registry) + } + request("attach", + version = sprintf("%s.%s", R.version$major, R.version$minor), + tempdir = tempdir, + info = list( + command = commandArgs()[[1L]], + version = R.version.string, + start_time = format(file.info(tempdir)$ctime) + ), + plot_url = if (identical(names(dev.cur()), "httpgd")) httpgd::hgd_url(), + server = if (use_webserver) list( + host = host, + port = port, + token = token + ) else NULL + ) +} + +path_to_uri <- function(path) { + if (length(path) == 0) { + return(character()) + } + path <- path.expand(path) + if (.Platform$OS.type == "windows") { + prefix <- "file:///" + path <- gsub("\\", "/", path, fixed = TRUE) + } else { + prefix <- "file://" + } + paste0(prefix, utils::URLencode(path)) +} + +request_browser <- function(url, title, ..., viewer) { + # Printing URL with specific port triggers + # auto port-forwarding under remote development + message("Browsing ", url) + request("browser", url = url, title = title, ..., viewer = viewer) +} + +show_browser <- function(url, title = url, ..., + viewer = getOption("vsc.browser", "Active")) { + proxy_uri <- Sys.getenv("VSCODE_PROXY_URI") + if (nzchar(proxy_uri)) { + is_base_path <- grepl("\\:\\d+$", url) + url <- sub("^https?\\://(127\\.0\\.0\\.1|localhost)(\\:)?", + sub("\\{\\{?port\\}\\}?/?", "", proxy_uri), url + ) + if (is_base_path) { + url <- paste0(url, "/") + } + } + if (grepl("^https?\\://(127\\.0\\.0\\.1|localhost)(\\:\\d+)?", url)) { + request_browser(url = url, title = title, ..., viewer = viewer) + } else if (grepl("^https?\\://", url)) { + message( + if (nzchar(proxy_uri)) { + "VSCode is not running on localhost but on a remote server.\n" + } else { + "VSCode WebView only supports showing local http content.\n" + }, + "Opening in external browser..." + ) + request_browser(url = url, title = title, ..., viewer = FALSE) + } else { + path <- sub("^file\\://", "", url) + if (file.exists(path)) { + path <- normalizePath(path, "/", mustWork = TRUE) + if (grepl("\\.html?$", path, ignore.case = TRUE)) { + message( + "VSCode WebView has restricted access to local file.\n", + "Opening in external browser..." + ) + request_browser(url = path_to_uri(path), + title = title, ..., viewer = FALSE + ) + } else { + request("dataview", source = "object", type = "txt", + title = title, file = path, viewer = viewer + ) + } + } else { + stop("File not exists") + } + } +} + +show_webview <- function(url, title, ..., viewer) { + if (!is.character(url)) { + real_url <- NULL + temp_viewer <- function(url, ...) { + real_url <<- url + } + op <- options(viewer = temp_viewer, page_viewer = temp_viewer) + on.exit(options(op)) + print(url) + if (is.character(real_url)) { + url <- real_url + } else { + stop("Invalid object") + } + } + proxy_uri <- Sys.getenv("VSCODE_PROXY_URI") + if (nzchar(proxy_uri)) { + is_base_path <- grepl("\\:\\d+$", url) + url <- sub("^https?\\://(127\\.0\\.0\\.1|localhost)(\\:)?", + sub("\\{\\{?port\\}\\}?/?", "", proxy_uri), url + ) + if (is_base_path) { + url <- paste0(url, "/") + } + } + if (grepl("^https?\\://(127\\.0\\.0\\.1|localhost)(\\:\\d+)?", url)) { + request_browser(url = url, title = title, ..., viewer = viewer) + } else if (grepl("^https?\\://", url)) { + message( + if (nzchar(proxy_uri)) { + "VSCode is not running on localhost but on a remote server.\n" + } else { + "VSCode WebView only supports showing local http content.\n" + }, + "Opening in external browser..." + ) + request_browser(url = url, title = title, ..., viewer = FALSE) + } else if (file.exists(url)) { + file <- normalizePath(url, "/", mustWork = TRUE) + request("webview", file = file, title = title, viewer = viewer, ...) + } else { + stop("File not exists") + } +} + +show_viewer <- function(url, title = NULL, ..., + viewer = getOption("vsc.viewer", "Two")) { + if (is.null(title)) { + expr <- substitute(url) + if (is.character(url)) { + title <- "Viewer" + } else { + title <- deparse(expr, nlines = 1) + } + } + show_webview(url = url, title = title, ..., viewer = viewer) +} + +show_page_viewer <- function(url, title = NULL, ..., + viewer = getOption("vsc.page_viewer", "Active")) { + if (is.null(title)) { + expr <- substitute(url) + if (is.character(url)) { + title <- "Page Viewer" + } else { + title <- deparse(expr, nlines = 1) + } + } + show_webview(url = url, title = title, ..., viewer = viewer) +} + +options( + browser = show_browser, + viewer = show_viewer, + page_viewer = show_page_viewer +) + +# rstudioapi +rstudioapi_enabled <- function() { + isTRUE(getOption("vsc.rstudioapi", TRUE)) +} + +if (rstudioapi_enabled()) { + response_timeout <- 5 + response_lock_file <- file.path(dir_session, "response.lock") + response_file <- file.path(dir_session, "response.log") + file.create(response_lock_file, showWarnings = FALSE) + file.create(response_file, showWarnings = FALSE) + addin_registry <- file.path(dir_session, "addins.json") + # This is created in attach() + + get_response_timestamp <- function() { + readLines(response_lock_file) + } + # initialise the reponse timestamp to empty string + response_time_stamp <- "" + + get_response_lock <- function() { + lock_time_stamp <- get_response_timestamp() + if (isTRUE(lock_time_stamp != response_time_stamp)) { + response_time_stamp <<- lock_time_stamp + TRUE + } else { + FALSE + } + } + + request_response <- function(command, ...) { + request(command, ..., sd = dir_session) + wait_start <- Sys.time() + while (!get_response_lock()) { + if ((Sys.time() - wait_start) > response_timeout) { + stop( + "Did not receive a response from VSCode-R API within ", + response_timeout, " seconds." + ) + } + Sys.sleep(0.1) + } + jsonlite::read_json(response_file) + } + + rstudioapi_util_env <- new.env() + rstudioapi_env <- new.env(parent = rstudioapi_util_env) + source(file.path(dir_init, "rstudioapi_util.R"), local = rstudioapi_util_env) + source(file.path(dir_init, "rstudioapi.R"), local = rstudioapi_env) + setHook( + packageEvent("rstudioapi", "onLoad"), + function(...) { + rstudioapi_util_env$rstudioapi_patch_hook(rstudioapi_env) + } + ) + if ("rstudioapi" %in% loadedNamespaces()) { + # if the rstudioapi is already loaded, for example via a call to + # library(tidyverse) in the user's profile, we need to shim it now. + # There's no harm in having also registered the hook in this case. It can + # work in the event that the namespace is unloaded and reloaded. + rstudioapi_util_env$rstudioapi_patch_hook(rstudioapi_env) + } + +} + +print.help_files_with_topic <- function(h, ...) { + viewer <- getOption("vsc.helpPanel", "Two") + if (!identical(FALSE, viewer) && length(h) >= 1 && is.character(h)) { + file <- h[1] + path <- dirname(file) + dirpath <- dirname(path) + pkgname <- basename(dirpath) + requestPath <- paste0( + "/library/", + pkgname, + "/html/", + basename(file), + ".html" + ) + request(command = "help", requestPath = requestPath, viewer = viewer) + } else { + utils:::print.help_files_with_topic(h, ...) + } + invisible(h) +} + +print.hsearch <- function(x, ...) { + viewer <- getOption("vsc.helpPanel", "Two") + if (!identical(FALSE, viewer) && length(x) >= 1) { + requestPath <- paste0( + "/doc/html/Search?pattern=", + tools:::escapeAmpersand(x$pattern), + paste0("&fields.", x$fields, "=1", + collapse = "" + ), + if (!is.null(x$agrep)) paste0("&agrep=", x$agrep), + if (!x$ignore.case) "&ignore.case=0", + if (!identical( + x$types, + getOption("help.search.types") + )) { + paste0("&types.", x$types, "=1", + collapse = "" + ) + }, + if (!is.null(x$package)) { + paste0( + "&package=", + paste(x$package, collapse = ";") + ) + }, + if (!identical(x$lib.loc, .libPaths())) { + paste0( + "&lib.loc=", + paste(x$lib.loc, collapse = ";") + ) + } + ) + request(command = "help", requestPath = requestPath, viewer = viewer) + } else { + utils:::print.hsearch(x, ...) + } + invisible(x) +} + +# a copy of .S3method(), since this function is new in R 4.0 +.S3method <- function(generic, class, method) { + if (missing(method)) { + method <- paste(generic, class, sep = ".") + } + method <- match.fun(method) + registerS3method(generic, class, method, envir = parent.frame()) + invisible(NULL) +} + +reg.finalizer(.GlobalEnv, function(e) .vsc$request("detach"), onexit = TRUE) diff --git a/R/template/R.gitignore b/R/template/R.gitignore new file mode 100644 index 000000000..e75435c1b --- /dev/null +++ b/R/template/R.gitignore @@ -0,0 +1,49 @@ +# History files +.Rhistory +.Rapp.history + +# Session Data files +.RData +.RDataTmp + +# User-specific files +.Ruserdata + +# Example code in package build process +*-Ex.R + +# Output files from R CMD build +/*.tar.gz + +# Output files from R CMD check +/*.Rcheck/ + +# RStudio files +.Rproj.user/ + +# produced vignettes +vignettes/*.html +vignettes/*.pdf + +# OAuth2 token, see https://github.com/hadley/httr/releases/tag/v0.3 +.httr-oauth + +# knitr and R markdown default cache directories +*_cache/ +/cache/ + +# Temporary files created by R markdown +*.utf8.md +*.knit.md + +# R Environment Variables +.Renviron + +# pkgdown site +docs/ + +# translation temp files +po/*~ + +# RStudio Connect folder +rsconnect/ diff --git a/README.md b/README.md index 6a9627dc7..a2a3671fa 100644 --- a/README.md +++ b/README.md @@ -1,93 +1,77 @@ -# R support for Visual Studio Code +# R Extension for Visual Studio Code -Requires [R](https://www.r-project.org/). +[![Badge](https://aka.ms/vsls-badge)](https://aka.ms/vsls) -## Usage +This [VS Code](https://code.visualstudio.com/) extension provides support for the [R programming language](https://www.r-project.org), including features such as R language service based on code analysis, interacting with R terminals, viewing data, plots, workspace variables, help pages, managing packages, and working with [R Markdown](https://rmarkdown.rstudio.com/) documents. -* For Windows, set config `r.rterm.windows` to your `R.exe` Path like `"C:\\Program Files\\R\\R-3.3.4\\bin\\x64\\R.exe"`; -* For Radian console, enable config `r.bracketedPaste` -* Open your *folder* that has R source file (**Can't work if you open only file**) -* Use `F1` key and `R:` command or `Ctrl+Enter`(Mac: `⌘+Enter`) +The R and R Markdown syntaxes are located in a slibing package [vscode-R-syntax](https://github.com/REditorSupport/vscode-R-syntax). -## Features +Go to the [wiki](https://github.com/REditorSupport/vscode-R/wiki) to view the documentation of the extension. + +## Getting started -* Run Source(`Ctrl+Shift+S` or Push icon![icon](images/FileDownload.png)) and Run Selected Line (`Ctrl+Enter`) -* Run `nrow`, `length`, `head`, `thead`, `names` functions (`Ctrl` + `1`, `2`, `3`, `4`, `5`) - * If you are using Mac `Ctrl` to `⌘` +1. [Install R](https://cloud.r-project.org/) (>= 3.4.0) on your system. For Windows users, Writing R Path to the registry is recommended in the installation. -![use Run .R](images/feature.png) +2. Install [`languageserver`](https://github.com/REditorSupport/languageserver) in R. -* R Integrated Terminal + ```r + install.packages("languageserver") + ``` -![Create R terminal](images/terminal.png) +3. Install the R extension for VS Code from the [VS Code Extension Marketplace](https://marketplace.visualstudio.com/items?itemName=reditorsupport.r) or the [Open VSX Registry](https://open-vsx.org/extension/reditorsupport/r). -* Run code in terminal containing existing R session, for example over SSH (`Run Selection/Line in Active Terminal`) -* Run all commands in terminal containing existing R session (enable config `r.alwaysUseActiveTerminal`) +4. Create an R file and start coding. -![R over SSH](images/ssh.gif) +The following software or extensions are recommended to enhance the experience of using R in VS Code: -* Extended Syntax(R, R Markdown, R Documentation) +* [radian](https://github.com/randy3k/radian): A modern R console that corrects many limitations of the official R terminal and supports many features such as syntax highlighting and auto-completion. -![Syntax](images/Rsyntax.png) +* [VSCode-R-Debugger](https://github.com/ManuelHentschel/VSCode-R-Debugger): A VS Code extension to support R debugging capabilities. -* Create .gitignore based [R.gitignore](https://github.com/github/gitignore/raw/master/R.gitignore) +* [httpgd](https://github.com/nx10/httpgd): An R package to provide a graphics device that asynchronously serves SVG graphics via HTTP and WebSockets. -* Data frame viewer and Environment viewer(`Preview Data frame` or `Preview Environment`) +Go to the installation wiki pages ([Windows](https://github.com/REditorSupport/vscode-R/wiki/Installation:-Windows) | [macOS](https://github.com/REditorSupport/vscode-R/wiki/Installation:-macOS) | [Linux](https://github.com/REditorSupport/vscode-R/wiki/Installation:-Linux)) for more detailed instructions. + +## Features -![Image](./images/DataframePreview.gif) +* Snippets for R and R Markdown. -* Snippets +* [R Language Service](https://github.com/REditorSupport/vscode-R/wiki/R-Language-Service): Code completion, function signature, symbol highlight, document outline, formatting, definition, diagnostics, references, and more. -* Package development short cut (`Load All`, `Test Package`, `Install Package`, `Build Package` and `Document`) +* [Interacting with R terminals](https://github.com/REditorSupport/vscode-R/wiki/Interacting-with-R-terminals): Sending code to terminals, running multiple terminals, working with remote servers. -## Requirements +* [Package development](https://github.com/REditorSupport/vscode-R/wiki/Package-development): Build, test, install, load all and other commands from devtools. -* R base from +* [Keyboard shortcuts](https://github.com/REditorSupport/vscode-R/wiki/Keyboard-shortcuts): Built-in and customizable keyboard shortcuts. -## Extension Settings +* [Workspace viewer](https://github.com/REditorSupport/vscode-R/wiki/Sidebar-user-interface#workspace-viewer): Environment pane to show global variables in the attached R session. -This extension contributes the following settings: +* [Help pages viewer](https://github.com/REditorSupport/vscode-R/wiki/Sidebar-user-interface#help-pages-viewer): Viewing R help pages and searching help topics. -* `r.rterm.windows`: set to R.exe path for Windows -* `r.rterm.mac`: set to R term's path for Mac OS X -* `r.rterm.linux`: set to R term's path for Linux -* `r.rpath.lsp`: set to R.exe path for Language Server Protocol -* `r.rterm.option`: R command line options (i.e: --vanilla) -* `r.source.encoding`: An optional encoding to pass to R when executing the file -* `r.source.focus`: Keeping focus when running (editor or terminal) -* `r.alwaysUseActiveTerminal`: Use active terminal for all commands, rather than creating a new R terminal -* `r.bracketedPaste`: For consoles supporting bracketed paste mode (such as Radian) +* [Package management](https://github.com/REditorSupport/vscode-R/wiki/Sidebar-user-interface#package-management): Installing and removing R packages. -* Language server(developing [here](https://github.com/REditorSupport/languageserver)) +* Session symbol hover and completion. -## TODO +* [Data viewer](https://github.com/REditorSupport/vscode-R/wiki/Interactive-viewers#data-viewer): Viewing `data.frame` or `matrix` in a grid or a list structure in a treeview. -* Output Plot -* Debug +* [Plot viewer](https://github.com/REditorSupport/vscode-R/wiki/Plot-viewer): PNG file viewer and SVG plot viewer based on [httpgd](https://github.com/nx10/httpgd). -## CONTRIBUTING +* [Webpage viewer](https://github.com/REditorSupport/vscode-R/wiki/Interactive-viewers#webpage-viewer): Viewing [htmlwidgets](https://www.htmlwidgets.org) such as interactive graphics and [visual profiling results](https://rstudio.github.io/profvis/). -* Please see [CONTRIBUTING.md](https://github.com/Ikuyadeu/vscode-R/blob/master/CONTRIBUTING.md) +* [Browser viewer](https://github.com/REditorSupport/vscode-R/wiki/Interactive-viewers#browser-viewer): Viewing interactive [shiny](https://shiny.rstudio.com) apps. -This extension based on +* [R Markdown support](https://github.com/REditorSupport/vscode-R/wiki/R-Markdown): R Markdown chunk highlighting, chunk navigation, execute commands, and preview. -* [r.tmbundle](https://github.com/textmate/r.tmbundle) -* [markdown-redcarpet.tmbundle](https://github.com/streeter/markdown-redcarpet.tmbundle) -* [Markdown extension in VS Code](https://github.com/Microsoft/vscode/blob/master/extensions/markdown/snippets/markdown.json) -* [R.gitignore](https://github.com/github/gitignore/raw/master/R.gitignore) -* [language-r](https://github.com/lee-dohm/language-r) -* [R box](https://github.com/randy3k/R-Box) +* [RStudio add-in support](https://github.com/REditorSupport/vscode-R/wiki/RStudio-addin-support): Run supported RStudio add-ins in VS Code with a live R session. -## Collaborators +* Full support of [Remote Development](https://code.visualstudio.com/docs/remote/remote-overview) via [SSH](https://code.visualstudio.com/docs/remote/ssh), [Containers](https://code.visualstudio.com/docs/remote/containers) and [WSL](https://code.visualstudio.com/docs/remote/wsl). -I hope you will join us. +* [Live share collaboration](https://github.com/REditorSupport/vscode-R/wiki/Live-share-collaboration): Shared workspace, terminal, and viewer in R pair programming. -* [@andycraig](https://github.com/andycraig) -* [@Ladvien](https://github.com/Ladvien) +## Questions, issues, feature requests, and contributions -## FAQ +* If you have a question about accomplishing something in general with the extension, please [ask on Stack Overflow](https://stackoverflow.com/questions/tagged/visual-studio-code+r). -* Q: I can't use command and message is `xxx no command found`. -* A: Please open your folder that has R source file +* If you find a problem or have a feature request with the extension, please [find out](https://github.com/REditorSupport/vscode-R/issues) if there is a current issue you may upvote or otherwise [file an issue](https://github.com/REditorSupport/vscode-R/issues/new/choose). -The R logo is © 2016 The R Foundation +* Contributions are always welcome! Please see the [contributing guide](https://github.com/REditorSupport/vscode-R/wiki/Contributing) for more details. diff --git a/cliff.toml b/cliff.toml new file mode 100644 index 000000000..e886c2151 --- /dev/null +++ b/cliff.toml @@ -0,0 +1,77 @@ +[remote.github] +owner = "REditorSupport" +repo = "vscode-R" + +[changelog] +header = """ +# Changelog + +""" +body = """ +{% if version %}\ + ## {{ version | trim_start_matches(pat="v") }} - {{ timestamp | date(format="%Y-%m-%d") }}\ +{% else %}\ + ## Unreleased\ +{% endif %}\ +{% for group, commits in commits | group_by(attribute="group") %} + + ### {{ group | upper_first }} + {% for commit in commits %} + {% set_global issues = [] %}\ + {% if commit.remote.pr_number %}\ + {% set_global issues = issues | concat(with=commit.remote.pr_number | as_str) %}\ + {%- endif %}\ + {% for link in commit.links %}\ + {% set_global issues = issues | concat(with=link.text | split(pat="#") | last) %}\ + {%- endfor -%}\ + {% if commit.remote.pr_title -%}\ + {%- set commit_message = commit.remote.pr_title -%}\ + {%- else -%}\ + {%- set commit_message = commit.message -%}\ + {%- endif -%}\ + * {{ commit_message | split(pat="\n") | first | trim }}\ + {% set_global issues = issues | unique %}\ + {% if issues | length > 0 %} (\ + {% for issue in issues %}\ + [#{{ issue }}](https://github.com/REditorSupport/vscode-R/issues/{{ issue }})\ + {% if not loop.last %}, {% endif %}\ + {%- endfor -%})\ + {%- endif %}\ + {%- endfor -%}\n +{% endfor %}\ +{% if version %} + {% if previous.version %} + **Full Changelog**: + {% endif %} +{% else -%} + {% raw %}\n{% endraw %} +{% endif %}\ +""" +trim = true +footer = """ +See [CHANGELOG.old.md](https://github.com/REditorSupport/vscode-R/blob/master/CHANGELOG.old.md) for changes before v2.8.5. + + +""" +postprocessors = [] + +[git] +conventional_commits = false +split_commits = false +commit_parsers = [ + { message = "(?i)^feat\\b", group = "Features" }, + { message = "(?i)^fix\\b", group = "Bug Fixes" }, + { message = "(?i)^docs\\b", group = "Documentation" }, + { message = "(?i)^perf\\b", group = "Performance" }, + { message = "(?i)^refactor\\b", group = "Refactor" }, + { message = "(?i)^style\\b", group = "Styling" }, + { message = "(?i)^test\\b", group = "Testing" }, + { message = "(?i)^(chore|bump to|Merge branch)\\b", skip = true }, + { body = ".*", group = "Other" }, +] +link_parsers = [ + { pattern = "(?i)\\b(?:close|closes|closed|fix|fixes|fixed|resolve|resolves|resolved) #(\\d+)", href = "https://github.com/REditorSupport/vscode-R/issues/$1" }, +] +filter_commits = true +topo_order = false +sort_commits = "oldest" diff --git a/esbuild.js b/esbuild.js new file mode 100644 index 000000000..b505d1d7e --- /dev/null +++ b/esbuild.js @@ -0,0 +1,61 @@ +const esbuild = require('esbuild'); +const fs = require('fs'); +const path = require('path'); + +const production = process.argv.includes('--production'); +const watch = process.argv.includes('--watch'); + +function copyResources() { + const destDir = path.join(__dirname, 'dist', 'resources'); + fs.mkdirSync(destDir, { recursive: true }); + + const resources = [ + './node_modules/jquery/dist/jquery.min.js', + './node_modules/jquery.json-viewer/json-viewer', + './node_modules/ag-grid-community/dist/ag-grid-community.min.noStyle.js', + './node_modules/ag-grid-community/styles/ag-grid.min.css', + './node_modules/ag-grid-community/styles/ag-theme-balham.min.css' + ]; + + for (const res of resources) { + const srcPath = path.resolve(__dirname, res); + const destName = path.basename(srcPath); + const destPath = path.resolve(destDir, destName); + if (fs.existsSync(srcPath)) { + fs.cpSync(srcPath, destPath, { recursive: true }); + } else { + console.warn(`Warning: Resource not found: ${srcPath}`); + } + } + console.log('Resources copied.'); +} + +async function main() { + copyResources(); + + const ctx = await esbuild.context({ + entryPoints: ['./src/extension.ts'], + bundle: true, + format: 'cjs', + minify: production, + sourcemap: !production, + sourcesContent: false, + platform: 'node', + outfile: 'dist/extension.js', + external: ['vscode', 'utf-8-validate', 'bufferutil'], + logLevel: 'info', + }); + + if (watch) { + await ctx.watch(); + console.log('Watching for changes...'); + } else { + await ctx.rebuild(); + await ctx.dispose(); + } +} + +main().catch(e => { + console.error(e); + process.exit(1); +}); diff --git a/html/help/00Index.ejs b/html/help/00Index.ejs new file mode 100644 index 000000000..be32cdd39 --- /dev/null +++ b/html/help/00Index.ejs @@ -0,0 +1,38 @@ + + + + + + <%= packageTitle %> + + + + + + +
+

+ <%= packageTitle %> +

+
+

Documentation for package ‘<%= packageName %> ’ version <%= packageVersion %> +

+ + + +

Help Pages

+ + + <% topics.forEach((topic)=> { %> + + + + + <% }) %> +
<%= topic.name %><%= topic.title %>
+
+ + + \ No newline at end of file diff --git a/html/help/script.ts b/html/help/script.ts new file mode 100644 index 000000000..8499bc456 --- /dev/null +++ b/html/help/script.ts @@ -0,0 +1,97 @@ +declare function acquireVsCodeApi(): VsCode; + +const vscode = acquireVsCodeApi(); + +// notify vscode when mouse buttons are clicked +// used to implement back/forward on mouse buttons 3/4 +window.onmousedown = (ev) => { + vscode.postMessage({ + message: 'mouseClick', + button: Number(ev.button), + scrollY: window.scrollY + }); +}; + + +// handle requests from vscode ui +window.addEventListener('message', (ev: MessageEvent) => { + // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment + const message = ev.data; + // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access + if(message.command === 'getScrollY'){ + vscode.postMessage({ + message: 'getScrollY', + scrollY: window.scrollY + }); + } +}); + + +// do everything after loading the body +window.document.body.onload = () => { + + // make relative path for hyperlinks + const relPath = (document.body.getAttribute('relPath') || ''); + + // notify vscode, used to restore help panels between sessions + vscode.setState(relPath); + + const loc = document.location; + const url0 = new URL(loc.protocol + '//' + loc.host); + const url1 = new URL(relPath, url0); + + // scroll to desired position: + const scrollYTo = Number(document.body.getAttribute('scrollYTo') ?? -1); + if(scrollYTo >= 0){ + window.scrollTo(0,scrollYTo); + } else if(url1.hash){ + document.location.hash = url1.hash; + } + + // notify vscode when links are clicked: + const hyperLinks = document.getElementsByTagName('a'); + + for(let i=0; i { + document.location.hash = hrefRel; + }; + } else if(hrefAbs && hrefAbs.startsWith('vscode-webview://')){ + hyperLinks[i].onclick = () => { + + const url2 = new URL(hrefRel, url1); + const finalHref = url2.toString(); + + vscode.postMessage({ + message: 'linkClicked', + href: finalHref, + scrollY: window.scrollY + }); + }; + } + } + + // notify vscode when code is clicked: + if(document.body.classList.contains('preClickable')){ + const codeElements = document.getElementsByTagName('pre'); + for(let i=0; i { + vscode.postMessage({ + message: 'codeClicked', + code: el.textContent || '', + modifiers: { + altKey: me.altKey, + ctrlKey: me.ctrlKey, + shiftKey: me.shiftKey, + metaKey: me.metaKey, + } + }); + }; + } + } +}; + diff --git a/html/help/theme.css b/html/help/theme.css new file mode 100644 index 000000000..8876556a5 --- /dev/null +++ b/html/help/theme.css @@ -0,0 +1,120 @@ + +/* General styling */ +body { + font-size: var(--vscode-editor-font-size); +} + +body table:nth-child(1)[width="100%"] td:nth-child(2){ + display: none; +} + +body table:nth-child(1)[width="100%"] td:nth-child(1){ + text-align: right; +} + +h1, h2 { + text-align: center; + margin-block-end: 0; +} + +img { + display: none; +} + +a ~ div.header { + display: none; +} + +/* Styling for preview info box */ +.previewInfo { + position: relative; + left: -20px; + width: calc(100% + 40px); + background-color: var(--vscode-list-inactiveSelectionBackground); + box-sizing: border-box; + text-align: center; + + padding-top: 0.5em; + padding-bottom: 0.5em; + padding-left: calc(0.5em + 20px); + padding-right: calc(0.5em + 20px); + /* margin-top: 1.5em; */ + margin-bottom: 1em; + + font-family: var(--vscode-editor-font-family); + font-size: var(--vscode-editor-font-size); +} + +/* Styling for clickable code sections */ +pre, code { + font-family: var(--vscode-editor-font-family); + font-size: var(--vscode-editor-font-size); +} + +.preClickable pre { + margin: 0px; + padding-top: 0.5em; + padding-bottom: 0.5em; +} + +.preHoverPointer .preDiv:hover { + cursor: pointer; +} +.preClickable .preCodeExample:hover { + background-color: var(--vscode-list-hoverBackground); +} + +/* Syntax highlighting in code sections */ +.hljs-link { + color: var(--vscode-textLink-foreground) +} + +.vscode-light { + --rhelp-number: #cb4b16; + --rhelp-string: #647400; + --rhelp-symbol: #cb4b16; + --rhelp-keyword: #2074b1; + --rhelp-comment: #727e7e; + --rhelp-function: #2074b1; +} + +.vscode-dark, +.vscode-high-contrast { + --rhelp-number: #b5cea8; + --rhelp-string: #CE9178; + --rhelp-symbol: #4EC9B0; + --rhelp-keyword: #569cd6; + --rhelp-comment: #6A9955; + --rhelp-function: #DCDCAA; +} + +.hljs-number { + color: var(--rhelp-number) +} + +.hljs-regexp, +.hljs-bullet, +.hljs-string { + color: var(--rhelp-string) +} + +.hljs-symbol, +.hljs-class { + color: var(--rhelp-symbol) +} + +.hljs-literal, +.hljs-keyword { + color: var(--rhelp-keyword) +} + +.hljs-built_in, +.hljs-function { + color: var(--rhelp-function) +} + +.hljs-quote, +.hljs-comment { + color: var(--rhelp-comment) +} + diff --git a/html/help/tsconfig.json b/html/help/tsconfig.json new file mode 100644 index 000000000..a3421d71d --- /dev/null +++ b/html/help/tsconfig.json @@ -0,0 +1,17 @@ + +{ + "compilerOptions": { + "module": "commonjs", + "target": "es2018", + "lib": [ + "es2018", + "DOM" + ], + "sourceMap": false, + "rootDir": ".", + "strict": true, + "noImplicitAny": true, + "noImplicitThis": true, + "noFallthroughCasesInSwitch": true + } +} diff --git a/html/help/webviewMessages.d.ts b/html/help/webviewMessages.d.ts new file mode 100644 index 000000000..19e5ceaaa --- /dev/null +++ b/html/help/webviewMessages.d.ts @@ -0,0 +1,42 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ + +interface VsCode { + postMessage: (msg: OutMessage) => void; + setState: (state: string) => void; +} + + +interface IOutMessage { + message: string; +} +interface LogMessage extends IOutMessage { + message: 'log', + body: any +} +interface MouseClickMessage extends IOutMessage { + message: 'mouseClick', + button: number, + scrollY: number +} +interface LinkClickedMessage extends IOutMessage { + message: 'linkClicked', + href: string, + scrollY: number +} +interface CodeClickedMessage extends IOutMessage { + message: 'codeClicked', + code: string, + modifiers: { + altKey: boolean, + ctrlKey: boolean, + shiftKey: boolean, + metaKey: boolean, + } +} +interface GetScrollYMessage extends IOutMessage { + message: 'getScrollY', + scrollY: number +} + +type OutMessage = LogMessage | MouseClickMessage | LinkClickedMessage | CodeClickedMessage | GetScrollYMessage; + diff --git a/html/httpgd/index.ejs b/html/httpgd/index.ejs new file mode 100644 index 000000000..99036c41c --- /dev/null +++ b/html/httpgd/index.ejs @@ -0,0 +1,25 @@ + + + + + + > + + + +
> + <%- largePlot?.data %> +
+
+
+ <% plots.forEach((plot)=> { %> +
+ <%- include(asLocalPath('./smallPlot.ejs'), {plot: plot}) %> +
+ <% }) %> +
+
+ + + + diff --git a/html/httpgd/index.ts b/html/httpgd/index.ts new file mode 100644 index 000000000..401505733 --- /dev/null +++ b/html/httpgd/index.ts @@ -0,0 +1,247 @@ +/* eslint-disable @typescript-eslint/no-non-null-assertion */ +/* eslint-disable @typescript-eslint/no-unsafe-assignment */ +/* eslint-disable @typescript-eslint/ban-ts-comment */ +/* eslint-disable @typescript-eslint/no-unsafe-call */ +/* eslint-disable @typescript-eslint/no-explicit-any */ + + +interface Plot { + // unique ID for this plot (w.r.t. this connection/device) + id: string; + + // svg of the plot + svg: string; + + height?: number; + width?: number; +} + +// get vscode api +declare function acquireVsCodeApi(): VsCode; +const vscode = acquireVsCodeApi(); + +// globals +let oldHeight = -1; +let oldWidth = -1; + + +const handler = document.querySelector('#handler') as HTMLDivElement; +const largePlotDiv = document.querySelector('#largePlot') as HTMLDivElement; +const largeSvg = largePlotDiv.querySelector('svg') as SVGElement; +const cssLink = document.querySelector('link.overwrites') as HTMLLinkElement; +const smallPlotDiv = document.querySelector('#smallPlots') as HTMLDivElement; + + +function getSmallPlots(): HTMLAnchorElement[] { + const smallPlots: HTMLAnchorElement[] = []; + document.querySelectorAll('a.focusPlot').forEach(elm => { + smallPlots.push(elm as HTMLAnchorElement); + }); + return smallPlots; +} + +let isHandlerDragging = false; + + +let isFullWindow = false; + +function postResizeMessage(userTriggered: boolean = false){ + let newHeight = largePlotDiv.clientHeight; + let newWidth = largePlotDiv.clientWidth; + if(isFullWindow){ + newHeight = window.innerHeight; + newWidth = window.innerWidth; + } + if(userTriggered || newHeight !== oldHeight || newWidth !== oldWidth){ + const msg: ResizeMessage = { + message: 'resize', + height: newHeight, + width: newWidth, + userTriggered: userTriggered + }; + vscode.postMessage(msg); + oldHeight = newHeight; + oldWidth = newWidth; + } +} + +function postLogMessage(content: any){ + console.log(content); + vscode.postMessage({ + message: 'log', + body: content + }); +} + +window.addEventListener('message', (ev: MessageEvent) => { + const msg = ev.data; + if(msg.message === 'updatePlot'){ + updatePlot({ + id: String(msg.plotId), + svg: msg.svg + }); + } else if(msg.message === 'focusPlot'){ + focusPlot(String(msg.plotId)); + } else if(msg.message === 'toggleStyle'){ + toggleStyle(msg.useOverwrites); + } else if(msg.message === 'hidePlot'){ + hidePlot(msg.plotId); + } else if(msg.message === 'addPlot'){ + addPlot(msg.html); + } else if(msg.message === 'togglePreviewPlotLayout'){ + togglePreviewPlotLayout(msg.style); + } else if(msg.message === 'toggleFullWindow'){ + toggleFullWindowMode(msg.useFullWindow); + } +}); + +function addPlot(html: string){ + const wrapper = document.createElement('div'); + wrapper.classList.add('wrapper'); + wrapper.innerHTML = html; + smallPlotDiv.appendChild(wrapper); +} + +function focusPlot(plotId: string): void { + + const smallPlots = getSmallPlots(); + + const ind = findIndex(plotId, smallPlots); + if(ind < 0){ + return; + } + + for(const elm of smallPlots){ + elm.classList.remove('active'); + } + + const smallPlot = smallPlots[ind]; + + smallPlot.classList.add('active'); + + largePlotDiv.innerHTML = smallPlot.innerHTML; +} + +function updatePlot(plt: Plot): void { + + const smallPlots = getSmallPlots(); + + const ind = findIndex(plt.id, smallPlots); + if(ind<0){ + return; + } + + smallPlots[ind].innerHTML = plt.svg; + + if(smallPlots[ind].classList.contains('active')){ + largePlotDiv.innerHTML = plt.svg; + } +} + +function hidePlot(plotId: string): void { + const smallPlots = getSmallPlots(); + + const ind = findIndex(plotId, smallPlots); + if(ind<0){ + return; + } + + if(smallPlots[ind].classList.contains('active')){ + largePlotDiv.innerHTML = ''; + } + + smallPlots[ind].parentElement?.remove(); +} + +function findIndex(plotId: string, smallPlots?: Element[]): number { + smallPlots ||= getSmallPlots(); + const ind = smallPlots.findIndex(elm => elm.getAttribute('plotId') === String(plotId)); + if(ind<0){ + console.warn(`plotId not found: ${plotId}`); + } + return ind; +} + +function toggleStyle(useOverwrites: boolean): void { + cssLink.disabled = !useOverwrites; +} + +function togglePreviewPlotLayout(newStyle: PreviewPlotLayout): void { + smallPlotDiv.classList.remove('multirow', 'scroll', 'hidden'); + smallPlotDiv.classList.add(newStyle); +} + +function toggleFullWindowMode(useFullWindow: boolean): void { + isFullWindow = useFullWindow; + if(useFullWindow){ + document.body.classList.add('fullWindow'); + window.scrollTo(0, 0); + } else { + document.body.classList.remove('fullWindow'); + } + postResizeMessage(true); +} + +//// +// On window load +//// + +window.onload = () => { + largePlotDiv.style.height = `${largeSvg.clientHeight}px`; + postResizeMessage(true); +}; + + +//// +// Resize bar +//// + + +document.addEventListener('mousedown', (e) => { + // If mousedown event is fired from .handler, toggle flag to true + if (!isFullWindow && e.target === handler) { + isHandlerDragging = true; + handler.classList.add('dragging'); + document.body.style.cursor = 'ns-resize'; + } +}); + +document.addEventListener('mousemove', (e) => { + // Don't do anything if dragging flag is false + if (isFullWindow || !isHandlerDragging) { + return false; + } + + // postLogMessage('mousemove'); + + // Get offset + const containerOffsetTop = document.body.offsetTop; + + // Get x-coordinate of pointer relative to container + const pointerRelativeYpos = e.clientY - containerOffsetTop + window.scrollY; + + // Arbitrary minimum width set on box A, otherwise its inner content will collapse to width of 0 + const largePlotMinHeight = 60; + + // Resize large plot + const newHeight = Math.max(largePlotMinHeight, pointerRelativeYpos - 5); // <- why 5? + const newHeightString = `${newHeight}px`; + + if(largePlotDiv.style.height !== newHeightString){ + largePlotDiv.style.height = newHeightString; + postResizeMessage(); + } +}); + +window.onresize = () => postResizeMessage(); + +document.addEventListener('mouseup', () => { + // Turn off dragging flag when user mouse is up + if(isHandlerDragging){ + postResizeMessage(true); + document.body.style.cursor = ''; + } + handler.classList.remove('dragging'); + isHandlerDragging = false; +}); + diff --git a/html/httpgd/smallPlot.ejs b/html/httpgd/smallPlot.ejs new file mode 100644 index 000000000..7d8337661 --- /dev/null +++ b/html/httpgd/smallPlot.ejs @@ -0,0 +1,15 @@ + + <%- plot.data %> + + + ✖ + diff --git a/html/httpgd/style.css b/html/httpgd/style.css new file mode 100644 index 000000000..c7d30c9b4 --- /dev/null +++ b/html/httpgd/style.css @@ -0,0 +1,136 @@ + +/* Use box-sizing: border-box everywhere: */ +html { + box-sizing: border-box; +} +*, *::before, *::after { + box-sizing: inherit; +} + +/* General styling: */ +body { + padding-left: 1px; + padding-right: 1px; +} +body.fullWindow { + overflow-x: hidden; + overflow-y: hidden; +} + +svg { + user-select: none; +} + +/* Stretch large plot during resizing, */ +/* Keep small plots the same size: */ +.httpgd { + width: 100%; + height: 100%; +} + +/* Main plot area: */ +#largePlot { + overflow-x: auto; + overflow-y: hidden; + padding: 10px; + width: 100%; + height: 100%; +} +body.fullWindow #largePlot { + overflow-x: hidden; + width: 100vw !important; + height: 100vh !important; +} + +/* Dragbar to resize main plot: */ +#handler { + background-color: var(--vscode-textSeparator-foreground); + height: 4px; + cursor: ns-resize; +} +#handler:hover, #handler.dragging { + background-color: var(--vscode-focusBorder); + transition: background-color .1s ease-out; + transition-delay: .2s; +} +body.fullWindow #handler { + display: none; +} + +#placeholder { + height: 95vh; +} +body.fullWindow #placeHolder { + display: none; +} + +/* Plot history: */ + +#smallPlots { + display: flex; + /* flex-direction: row; */ + position: relative; + overflow-x: auto; + flex-direction: row; + overflow-y: hidden; + padding: 10px; +} +body.fullWindow #smallPlots { + display: none; +} + +#smallPlots.multirow { + overflow-x: hidden; + flex-wrap: wrap; +} + +#smallPlots.hidden { + display: none; +} + +/* Each small plot: */ + +#smallPlots .wrapper { + position: relative; + height: 15vw; + width: 19vw; + flex: none; + padding: 3px; + padding-bottom: 12px; +} + +a.focusPlot { + height: 100%; + width: 100%; +} + +a.hidePlot { + display: none; + position: absolute; + top: 0; + right: 5px; + text-decoration: none; + font-size: 2em; + user-select: none; + color: var(--vscode-foreground); +} + +.plotContent { + height: 100%; + width: 100%; +} + +.smallPlot:not(.active):hover { + background-color: var(--vscode-list-hoverBackground); + background-clip: content-box; +} + +/* Hide plot button: */ +#smallPlots .wrapper:hover a.hidePlot { + display: block; +} + +#smallPlots .wrapper a.hidePlot:hover { + color: var(--vscode-errorForeground); +} + diff --git a/html/httpgd/styleOverwrites.css b/html/httpgd/styleOverwrites.css new file mode 100644 index 000000000..953ac5611 --- /dev/null +++ b/html/httpgd/styleOverwrites.css @@ -0,0 +1,15 @@ + + +.httpgd rect { + stroke: none !important; + fill: none !important; +} + +svg text { + font-family: var(--vscode-editor-font-family) !important; + fill: var(--vscode-foreground) !important; +} + +.httpgd line, .httpgd polyline, .httpgd polygon, .httpgd path, .httpgd circle, .httpgd rect:not(:first-of-type) { + stroke: var(--vscode-foreground) !important; +} diff --git a/html/httpgd/tsconfig.json b/html/httpgd/tsconfig.json new file mode 100644 index 000000000..a3421d71d --- /dev/null +++ b/html/httpgd/tsconfig.json @@ -0,0 +1,17 @@ + +{ + "compilerOptions": { + "module": "commonjs", + "target": "es2018", + "lib": [ + "es2018", + "DOM" + ], + "sourceMap": false, + "rootDir": ".", + "strict": true, + "noImplicitAny": true, + "noImplicitThis": true, + "noFallthroughCasesInSwitch": true + } +} diff --git a/html/httpgd/vars.css b/html/httpgd/vars.css new file mode 100644 index 000000000..d20aad6b3 --- /dev/null +++ b/html/httpgd/vars.css @@ -0,0 +1,470 @@ +/* This file contains a list of variables available in a vscode webview and their values from the Dark+ theme */ + +.fromvscode { + --vscode-font-family: "Segoe WPC", "Segoe UI", sans-serif; + --vscode-font-weight: normal; + --vscode-font-size: 13px; + --vscode-editor-font-family: Consolas, "Courier New", monospace; + --vscode-editor-font-weight: normal; + --vscode-editor-font-size: 14px; + --vscode-foreground: #cccccc; + --vscode-errorForeground: #f48771; + --vscode-descriptionForeground: rgba(204, 204, 204, 0.7); + --vscode-icon-foreground: #c5c5c5; + --vscode-focusBorder: #007fd4; + --vscode-textSeparator-foreground: rgba(255, 255, 255, 0.18); + --vscode-textLink-foreground: #3794ff; + --vscode-textLink-activeForeground: #3794ff; + --vscode-textPreformat-foreground: #d7ba7d; + --vscode-textBlockQuote-background: rgba(127, 127, 127, 0.1); + --vscode-textBlockQuote-border: rgba(0, 122, 204, 0.5); + --vscode-textCodeBlock-background: rgba(10, 10, 10, 0.4); + --vscode-widget-shadow: rgba(0, 0, 0, 0.36); + --vscode-input-background: #3c3c3c; + --vscode-input-foreground: #cccccc; + --vscode-inputOption-activeBorder: rgba(0, 122, 204, 0); + --vscode-inputOption-activeBackground: rgba(0, 127, 212, 0.4); + --vscode-inputOption-activeForeground: #ffffff; + --vscode-input-placeholderForeground: #a6a6a6; + --vscode-inputValidation-infoBackground: #063b49; + --vscode-inputValidation-infoBorder: #007acc; + --vscode-inputValidation-warningBackground: #352a05; + --vscode-inputValidation-warningBorder: #b89500; + --vscode-inputValidation-errorBackground: #5a1d1d; + --vscode-inputValidation-errorBorder: #be1100; + --vscode-dropdown-background: #3c3c3c; + --vscode-dropdown-foreground: #f0f0f0; + --vscode-dropdown-border: #3c3c3c; + --vscode-checkbox-background: #3c3c3c; + --vscode-checkbox-foreground: #f0f0f0; + --vscode-checkbox-border: #3c3c3c; + --vscode-button-foreground: #ffffff; + --vscode-button-background: #0e639c; + --vscode-button-hoverBackground: #1177bb; + --vscode-button-secondaryForeground: #ffffff; + --vscode-button-secondaryBackground: #3a3d41; + --vscode-button-secondaryHoverBackground: #45494e; + --vscode-badge-background: #4d4d4d; + --vscode-badge-foreground: #ffffff; + --vscode-scrollbar-shadow: #000000; + --vscode-scrollbarSlider-background: rgba(121, 121, 121, 0.4); + --vscode-scrollbarSlider-hoverBackground: rgba(100, 100, 100, 0.7); + --vscode-scrollbarSlider-activeBackground: rgba(191, 191, 191, 0.4); + --vscode-progressBar-background: #0e70c0; + --vscode-editorError-foreground: #f48771; + --vscode-editorWarning-foreground: #cca700; + --vscode-editorInfo-foreground: #75beff; + --vscode-editorHint-foreground: rgba(238, 238, 238, 0.7); + --vscode-sash-hoverBorder: #007fd4; + --vscode-editor-background: #1e1e1e; + --vscode-editor-foreground: #d4d4d4; + --vscode-editorWidget-background: #252526; + --vscode-editorWidget-foreground: #cccccc; + --vscode-editorWidget-border: #454545; + --vscode-quickInput-background: #252526; + --vscode-quickInput-foreground: #cccccc; + --vscode-quickInputTitle-background: rgba(255, 255, 255, 0.1); + --vscode-pickerGroup-foreground: #3794ff; + --vscode-pickerGroup-border: #3f3f46; + --vscode-editor-selectionBackground: #264f78; + --vscode-editor-inactiveSelectionBackground: #3a3d41; + --vscode-editor-selectionHighlightBackground: rgba(173, 214, 255, 0.15); + --vscode-editor-findMatchBackground: #515c6a; + --vscode-editor-findMatchHighlightBackground: rgba(234, 92, 0, 0.33); + --vscode-editor-findRangeHighlightBackground: rgba(58, 61, 65, 0.4); + --vscode-searchEditor-findMatchBackground: rgba(234, 92, 0, 0.22); + --vscode-editor-hoverHighlightBackground: rgba(38, 79, 120, 0.25); + --vscode-editorHoverWidget-background: #252526; + --vscode-editorHoverWidget-foreground: #cccccc; + --vscode-editorHoverWidget-border: #454545; + --vscode-editorHoverWidget-statusBarBackground: #2c2c2d; + --vscode-editorLink-activeForeground: #4e94ce; + --vscode-editorInlineHint-foreground: #252526; + --vscode-editorInlineHint-background: #cccccc; + --vscode-editorLightBulb-foreground: #ffcc00; + --vscode-editorLightBulbAutoFix-foreground: #75beff; + --vscode-diffEditor-insertedTextBackground: rgba(155, 185, 85, 0.2); + --vscode-diffEditor-removedTextBackground: rgba(255, 0, 0, 0.2); + --vscode-diffEditor-diagonalFill: rgba(204, 204, 204, 0.2); + --vscode-list-focusOutline: #007fd4; + --vscode-list-activeSelectionBackground: #094771; + --vscode-list-activeSelectionForeground: #ffffff; + --vscode-list-inactiveSelectionBackground: #37373d; + --vscode-list-hoverBackground: #2a2d2e; + --vscode-list-dropBackground: #383b3d; + --vscode-list-highlightForeground: #0097fb; + --vscode-list-invalidItemForeground: #b89500; + --vscode-list-errorForeground: #f88070; + --vscode-list-warningForeground: #cca700; + --vscode-listFilterWidget-background: #653723; + --vscode-listFilterWidget-outline: rgba(0, 0, 0, 0); + --vscode-listFilterWidget-noMatchesOutline: #be1100; + --vscode-list-filterMatchBackground: rgba(234, 92, 0, 0.33); + --vscode-tree-indentGuidesStroke: #585858; + --vscode-tree-tableColumnsBorder: rgba(204, 204, 204, 0.13); + --vscode-list-deemphasizedForeground: #8c8c8c; + --vscode-quickInputList-focusBackground: #062f4a; + --vscode-menu-foreground: #cccccc; + --vscode-menu-background: #252526; + --vscode-menu-selectionForeground: #ffffff; + --vscode-menu-selectionBackground: #094771; + --vscode-menu-separatorBackground: #bbbbbb; + --vscode-toolbar-hoverBackground: rgba(90, 93, 94, 0.31); + --vscode-toolbar-activeBackground: rgba(99, 102, 103, 0.31); + --vscode-editor-snippetTabstopHighlightBackground: rgba(124, 124, 124, 0.3); + --vscode-editor-snippetFinalTabstopHighlightBorder: #525252; + --vscode-breadcrumb-foreground: rgba(204, 204, 204, 0.8); + --vscode-breadcrumb-background: #1e1e1e; + --vscode-breadcrumb-focusForeground: #e0e0e0; + --vscode-breadcrumb-activeSelectionForeground: #e0e0e0; + --vscode-breadcrumbPicker-background: #252526; + --vscode-merge-currentHeaderBackground: rgba(64, 200, 174, 0.5); + --vscode-merge-currentContentBackground: rgba(64, 200, 174, 0.2); + --vscode-merge-incomingHeaderBackground: rgba(64, 166, 255, 0.5); + --vscode-merge-incomingContentBackground: rgba(64, 166, 255, 0.2); + --vscode-merge-commonHeaderBackground: rgba(96, 96, 96, 0.4); + --vscode-merge-commonContentBackground: rgba(96, 96, 96, 0.16); + --vscode-editorOverviewRuler-currentContentForeground: rgba(64, 200, 174, 0.5); + --vscode-editorOverviewRuler-incomingContentForeground: rgba(64, 166, 255, 0.5); + --vscode-editorOverviewRuler-commonContentForeground: rgba(96, 96, 96, 0.4); + --vscode-editorOverviewRuler-findMatchForeground: rgba(209, 134, 22, 0.49); + --vscode-editorOverviewRuler-selectionHighlightForeground: rgba(160, 160, 160, 0.8); + --vscode-minimap-findMatchHighlight: #d18616; + --vscode-minimap-selectionHighlight: #264f78; + --vscode-minimap-errorHighlight: rgba(255, 18, 18, 0.7); + --vscode-minimap-warningHighlight: #cca700; + --vscode-minimapSlider-background: rgba(121, 121, 121, 0.2); + --vscode-minimapSlider-hoverBackground: rgba(100, 100, 100, 0.35); + --vscode-minimapSlider-activeBackground: rgba(191, 191, 191, 0.2); + --vscode-problemsErrorIcon-foreground: #f48771; + --vscode-problemsWarningIcon-foreground: #cca700; + --vscode-problemsInfoIcon-foreground: #75beff; + --vscode-charts-foreground: #cccccc; + --vscode-charts-lines: rgba(204, 204, 204, 0.5); + --vscode-charts-red: #f48771; + --vscode-charts-blue: #75beff; + --vscode-charts-yellow: #cca700; + --vscode-charts-orange: #d18616; + --vscode-charts-green: #89d185; + --vscode-charts-purple: #b180d7; + --vscode-editor-lineHighlightBorder: #282828; + --vscode-editor-rangeHighlightBackground: rgba(255, 255, 255, 0.04); + --vscode-editor-symbolHighlightBackground: rgba(234, 92, 0, 0.33); + --vscode-editorCursor-foreground: #aeafad; + --vscode-editorWhitespace-foreground: rgba(227, 228, 226, 0.16); + --vscode-editorIndentGuide-background: #404040; + --vscode-editorIndentGuide-activeBackground: #707070; + --vscode-editorLineNumber-foreground: #858585; + --vscode-editorActiveLineNumber-foreground: #c6c6c6; + --vscode-editorLineNumber-activeForeground: #c6c6c6; + --vscode-editorRuler-foreground: #5a5a5a; + --vscode-editorCodeLens-foreground: #999999; + --vscode-editorBracketMatch-background: rgba(0, 100, 0, 0.1); + --vscode-editorBracketMatch-border: #888888; + --vscode-editorOverviewRuler-border: rgba(127, 127, 127, 0.3); + --vscode-editorGutter-background: #1e1e1e; + --vscode-editorUnnecessaryCode-opacity: rgba(0, 0, 0, 0.67); + --vscode-editorOverviewRuler-rangeHighlightForeground: rgba(0, 122, 204, 0.6); + --vscode-editorOverviewRuler-errorForeground: rgba(255, 18, 18, 0.7); + --vscode-editorOverviewRuler-warningForeground: #cca700; + --vscode-editorOverviewRuler-infoForeground: #75beff; + --vscode-symbolIcon-arrayForeground: #cccccc; + --vscode-symbolIcon-booleanForeground: #cccccc; + --vscode-symbolIcon-classForeground: #ee9d28; + --vscode-symbolIcon-colorForeground: #cccccc; + --vscode-symbolIcon-constantForeground: #cccccc; + --vscode-symbolIcon-constructorForeground: #b180d7; + --vscode-symbolIcon-enumeratorForeground: #ee9d28; + --vscode-symbolIcon-enumeratorMemberForeground: #75beff; + --vscode-symbolIcon-eventForeground: #ee9d28; + --vscode-symbolIcon-fieldForeground: #75beff; + --vscode-symbolIcon-fileForeground: #cccccc; + --vscode-symbolIcon-folderForeground: #cccccc; + --vscode-symbolIcon-functionForeground: #b180d7; + --vscode-symbolIcon-interfaceForeground: #75beff; + --vscode-symbolIcon-keyForeground: #cccccc; + --vscode-symbolIcon-keywordForeground: #cccccc; + --vscode-symbolIcon-methodForeground: #b180d7; + --vscode-symbolIcon-moduleForeground: #cccccc; + --vscode-symbolIcon-namespaceForeground: #cccccc; + --vscode-symbolIcon-nullForeground: #cccccc; + --vscode-symbolIcon-numberForeground: #cccccc; + --vscode-symbolIcon-objectForeground: #cccccc; + --vscode-symbolIcon-operatorForeground: #cccccc; + --vscode-symbolIcon-packageForeground: #cccccc; + --vscode-symbolIcon-propertyForeground: #cccccc; + --vscode-symbolIcon-referenceForeground: #cccccc; + --vscode-symbolIcon-snippetForeground: #cccccc; + --vscode-symbolIcon-stringForeground: #cccccc; + --vscode-symbolIcon-structForeground: #cccccc; + --vscode-symbolIcon-textForeground: #cccccc; + --vscode-symbolIcon-typeParameterForeground: #cccccc; + --vscode-symbolIcon-unitForeground: #cccccc; + --vscode-symbolIcon-variableForeground: #75beff; + --vscode-editorOverviewRuler-bracketMatchForeground: #a0a0a0; + --vscode-editor-linkedEditingBackground: rgba(255, 0, 0, 0.3); + --vscode-editor-wordHighlightBackground: rgba(87, 87, 87, 0.72); + --vscode-editor-wordHighlightStrongBackground: rgba(0, 73, 114, 0.72); + --vscode-editorOverviewRuler-wordHighlightForeground: rgba(160, 160, 160, 0.8); + --vscode-editorOverviewRuler-wordHighlightStrongForeground: rgba(192, 160, 192, 0.8); + --vscode-editor-foldBackground: rgba(38, 79, 120, 0.3); + --vscode-editorGutter-foldingControlForeground: #c5c5c5; + --vscode-peekViewTitle-background: #1e1e1e; + --vscode-peekViewTitleLabel-foreground: #ffffff; + --vscode-peekViewTitleDescription-foreground: rgba(204, 204, 204, 0.7); + --vscode-peekView-border: #007acc; + --vscode-peekViewResult-background: #252526; + --vscode-peekViewResult-lineForeground: #bbbbbb; + --vscode-peekViewResult-fileForeground: #ffffff; + --vscode-peekViewResult-selectionBackground: rgba(51, 153, 255, 0.2); + --vscode-peekViewResult-selectionForeground: #ffffff; + --vscode-peekViewEditor-background: #001f33; + --vscode-peekViewEditorGutter-background: #001f33; + --vscode-peekViewResult-matchHighlightBackground: rgba(234, 92, 0, 0.3); + --vscode-peekViewEditor-matchHighlightBackground: rgba(255, 143, 0, 0.6); + --vscode-editorMarkerNavigationError-background: #f48771; + --vscode-editorMarkerNavigationWarning-background: #cca700; + --vscode-editorMarkerNavigationInfo-background: #75beff; + --vscode-editorMarkerNavigation-background: #2d2d30; + --vscode-editorSuggestWidget-background: #252526; + --vscode-editorSuggestWidget-border: #454545; + --vscode-editorSuggestWidget-foreground: #d4d4d4; + --vscode-editorSuggestWidget-selectedBackground: #062f4a; + --vscode-editorSuggestWidget-highlightForeground: #0097fb; + --vscode-tab-activeBackground: #1e1e1e; + --vscode-tab-unfocusedActiveBackground: #1e1e1e; + --vscode-tab-inactiveBackground: #2d2d2d; + --vscode-tab-unfocusedInactiveBackground: #2d2d2d; + --vscode-tab-activeForeground: #ffffff; + --vscode-tab-inactiveForeground: rgba(255, 255, 255, 0.5); + --vscode-tab-unfocusedActiveForeground: rgba(255, 255, 255, 0.5); + --vscode-tab-unfocusedInactiveForeground: rgba(255, 255, 255, 0.25); + --vscode-tab-border: #252526; + --vscode-tab-lastPinnedBorder: rgba(204, 204, 204, 0.2); + --vscode-tab-activeModifiedBorder: #3399cc; + --vscode-tab-inactiveModifiedBorder: rgba(51, 153, 204, 0.5); + --vscode-tab-unfocusedActiveModifiedBorder: rgba(51, 153, 204, 0.5); + --vscode-tab-unfocusedInactiveModifiedBorder: rgba(51, 153, 204, 0.25); + --vscode-editorPane-background: #1e1e1e; + --vscode-editorGroupHeader-tabsBackground: #252526; + --vscode-editorGroupHeader-noTabsBackground: #1e1e1e; + --vscode-editorGroup-border: #444444; + --vscode-editorGroup-dropBackground: rgba(83, 89, 93, 0.5); + --vscode-imagePreview-border: rgba(128, 128, 128, 0.35); + --vscode-panel-background: #1e1e1e; + --vscode-panel-border: rgba(128, 128, 128, 0.35); + --vscode-panelTitle-activeForeground: #e7e7e7; + --vscode-panelTitle-inactiveForeground: rgba(231, 231, 231, 0.6); + --vscode-panelTitle-activeBorder: #e7e7e7; + --vscode-panel-dropBorder: #e7e7e7; + --vscode-panelSection-dropBackground: rgba(83, 89, 93, 0.5); + --vscode-panelSectionHeader-background: rgba(128, 128, 128, 0.2); + --vscode-panelSection-border: rgba(128, 128, 128, 0.35); + --vscode-statusBar-foreground: #ffffff; + --vscode-statusBar-noFolderForeground: #ffffff; + --vscode-statusBar-background: #007acc; + --vscode-statusBar-noFolderBackground: #68217a; + --vscode-statusBarItem-activeBackground: rgba(255, 255, 255, 0.18); + --vscode-statusBarItem-hoverBackground: rgba(255, 255, 255, 0.12); + --vscode-statusBarItem-prominentForeground: #ffffff; + --vscode-statusBarItem-prominentBackground: rgba(0, 0, 0, 0.5); + --vscode-statusBarItem-prominentHoverBackground: rgba(0, 0, 0, 0.3); + --vscode-statusBarItem-errorBackground: #c72e0f; + --vscode-statusBarItem-errorForeground: #ffffff; + --vscode-activityBar-background: #333333; + --vscode-activityBar-foreground: #ffffff; + --vscode-activityBar-inactiveForeground: rgba(255, 255, 255, 0.4); + --vscode-activityBar-activeBorder: #ffffff; + --vscode-activityBar-dropBorder: #ffffff; + --vscode-activityBarBadge-background: #007acc; + --vscode-activityBarBadge-foreground: #ffffff; + --vscode-statusBarItem-remoteBackground: #16825d; + --vscode-statusBarItem-remoteForeground: #ffffff; + --vscode-extensionBadge-remoteBackground: #007acc; + --vscode-extensionBadge-remoteForeground: #ffffff; + --vscode-sideBar-background: #252526; + --vscode-sideBarTitle-foreground: #bbbbbb; + --vscode-sideBar-dropBackground: rgba(83, 89, 93, 0.5); + --vscode-sideBarSectionHeader-background: rgba(0, 0, 0, 0); + --vscode-sideBarSectionHeader-border: rgba(204, 204, 204, 0.2); + --vscode-titleBar-activeForeground: #cccccc; + --vscode-titleBar-inactiveForeground: rgba(204, 204, 204, 0.6); + --vscode-titleBar-activeBackground: #3c3c3c; + --vscode-titleBar-inactiveBackground: rgba(60, 60, 60, 0.6); + --vscode-menubar-selectionForeground: #cccccc; + --vscode-menubar-selectionBackground: rgba(255, 255, 255, 0.1); + --vscode-notifications-foreground: #cccccc; + --vscode-notifications-background: #252526; + --vscode-notificationLink-foreground: #3794ff; + --vscode-notificationCenterHeader-background: #303031; + --vscode-notifications-border: #303031; + --vscode-notificationsErrorIcon-foreground: #f48771; + --vscode-notificationsWarningIcon-foreground: #cca700; + --vscode-notificationsInfoIcon-foreground: #75beff; + --vscode-editorGutter-commentRangeForeground: #c5c5c5; + --vscode-debugToolBar-background: #333333; + --vscode-debugIcon-startForeground: #89d185; + --vscode-settings-headerForeground: #e7e7e7; + --vscode-settings-modifiedItemIndicator: #0c7d9d; + --vscode-settings-dropdownBackground: #3c3c3c; + --vscode-settings-dropdownForeground: #f0f0f0; + --vscode-settings-dropdownBorder: #3c3c3c; + --vscode-settings-dropdownListBorder: #454545; + --vscode-settings-checkboxBackground: #3c3c3c; + --vscode-settings-checkboxForeground: #f0f0f0; + --vscode-settings-checkboxBorder: #3c3c3c; + --vscode-settings-textInputBackground: #3c3c3c; + --vscode-settings-textInputForeground: #cccccc; + --vscode-settings-numberInputBackground: #3c3c3c; + --vscode-settings-numberInputForeground: #cccccc; + --vscode-settings-focusedRowBackground: rgba(128, 128, 128, 0.14); + --vscode-notebook-rowHoverBackground: rgba(128, 128, 128, 0.07); + --vscode-notebook-focusedRowBorder: rgba(255, 255, 255, 0.12); + --vscode-terminal-foreground: #cccccc; + --vscode-terminal-selectionBackground: rgba(255, 255, 255, 0.25); + --vscode-terminal-border: rgba(128, 128, 128, 0.35); + --vscode-testing-iconFailed: #f14c4c; + --vscode-testing-iconErrored: #f14c4c; + --vscode-testing-iconPassed: #73c991; + --vscode-testing-runAction: #73c991; + --vscode-testing-iconQueued: #cca700; + --vscode-testing-iconUnset: #848484; + --vscode-testing-iconSkipped: #848484; + --vscode-testing-peekBorder: #f48771; + --vscode-testing-message\.error\.decorationForeground: #f48771; + --vscode-testing-message\.error\.lineBackground: rgba(255, 0, 0, 0.2); + --vscode-testing-message\.warning\.decorationForeground: #cca700; + --vscode-testing-message\.warning\.lineBackground: rgba(255, 208, 0, 0.2); + --vscode-testing-message\.info\.decorationForeground: #75beff; + --vscode-testing-message\.info\.lineBackground: rgba(0, 127, 255, 0.2); + --vscode-testing-message\.hint\.decorationForeground: rgba(238, 238, 238, 0.7); + --vscode-welcomePage-tileBackground: #252526; + --vscode-welcomePage-tileHoverBackground: #2c2c2d; + --vscode-welcomePage-tileShadow\.: rgba(0, 0, 0, 0.36); + --vscode-welcomePage-progress\.background: #3c3c3c; + --vscode-welcomePage-progress\.foreground: #3794ff; + --vscode-workspaceTrust-trustedForegound: #89d185; + --vscode-workspaceTrust-untrustedForeground: #f48771; + --vscode-workspaceTrust-tileBackground: #252526; + --vscode-statusBar-debuggingBackground: #cc6633; + --vscode-statusBar-debuggingForeground: #ffffff; + --vscode-debugExceptionWidget-border: #a31515; + --vscode-debugExceptionWidget-background: #420b0d; + --vscode-editorGutter-modifiedBackground: #0c7d9d; + --vscode-editorGutter-addedBackground: #587c0c; + --vscode-editorGutter-deletedBackground: #94151b; + --vscode-minimapGutter-modifiedBackground: #0c7d9d; + --vscode-minimapGutter-addedBackground: #587c0c; + --vscode-minimapGutter-deletedBackground: #94151b; + --vscode-editorOverviewRuler-modifiedForeground: rgba(12, 125, 157, 0.6); + --vscode-editorOverviewRuler-addedForeground: rgba(88, 124, 12, 0.6); + --vscode-editorOverviewRuler-deletedForeground: rgba(148, 21, 27, 0.6); + --vscode-notebook-cellBorderColor: #37373d; + --vscode-notebook-focusedEditorBorder: #007fd4; + --vscode-notebookStatusSuccessIcon-foreground: #89d185; + --vscode-notebookStatusErrorIcon-foreground: #f48771; + --vscode-notebookStatusRunningIcon-foreground: #cccccc; + --vscode-notebook-outputContainerBackgroundColor: #37373d; + --vscode-notebook-cellToolbarSeparator: rgba(128, 128, 128, 0.35); + --vscode-notebook-selectedCellBackground: #37373d; + --vscode-notebook-selectedCellBorder: #37373d; + --vscode-notebook-focusedCellBorder: #007fd4; + --vscode-notebook-inactiveFocusedCellBorder: #37373d; + --vscode-notebook-cellStatusBarItemHoverBackground: rgba(255, 255, 255, 0.15); + --vscode-notebook-cellInsertionIndicator: #007fd4; + --vscode-notebookScrollbarSlider-background: rgba(121, 121, 121, 0.4); + --vscode-notebookScrollbarSlider-hoverBackground: rgba(100, 100, 100, 0.7); + --vscode-notebookScrollbarSlider-activeBackground: rgba(191, 191, 191, 0.4); + --vscode-notebook-symbolHighlightBackground: rgba(255, 255, 255, 0.04); + --vscode-editor-stackFrameHighlightBackground: rgba(255, 255, 0, 0.2); + --vscode-editor-focusedStackFrameHighlightBackground: rgba(122, 189, 122, 0.3); + --vscode-debugIcon-breakpointForeground: #e51400; + --vscode-debugIcon-breakpointDisabledForeground: #848484; + --vscode-debugIcon-breakpointUnverifiedForeground: #848484; + --vscode-debugIcon-breakpointCurrentStackframeForeground: #ffcc00; + --vscode-debugIcon-breakpointStackframeForeground: #89d185; + --vscode-scm-providerBorder: #454545; + --vscode-extensionButton-prominentBackground: #0e639c; + --vscode-extensionButton-prominentForeground: #ffffff; + --vscode-extensionButton-prominentHoverBackground: #1177bb; + --vscode-extensionIcon-starForeground: #ff8e00; + --vscode-terminal-ansiBlack: #000000; + --vscode-terminal-ansiRed: #cd3131; + --vscode-terminal-ansiGreen: #0dbc79; + --vscode-terminal-ansiYellow: #e5e510; + --vscode-terminal-ansiBlue: #2472c8; + --vscode-terminal-ansiMagenta: #bc3fbc; + --vscode-terminal-ansiCyan: #11a8cd; + --vscode-terminal-ansiWhite: #e5e5e5; + --vscode-terminal-ansiBrightBlack: #666666; + --vscode-terminal-ansiBrightRed: #f14c4c; + --vscode-terminal-ansiBrightGreen: #23d18b; + --vscode-terminal-ansiBrightYellow: #f5f543; + --vscode-terminal-ansiBrightBlue: #3b8eea; + --vscode-terminal-ansiBrightMagenta: #d670d6; + --vscode-terminal-ansiBrightCyan: #29b8db; + --vscode-terminal-ansiBrightWhite: #e5e5e5; + --vscode-debugTokenExpression-name: #c586c0; + --vscode-debugTokenExpression-value: rgba(204, 204, 204, 0.6); + --vscode-debugTokenExpression-string: #ce9178; + --vscode-debugTokenExpression-boolean: #4e94ce; + --vscode-debugTokenExpression-number: #b5cea8; + --vscode-debugTokenExpression-error: #f48771; + --vscode-debugView-exceptionLabelForeground: #cccccc; + --vscode-debugView-exceptionLabelBackground: #6c2022; + --vscode-debugView-stateLabelForeground: #cccccc; + --vscode-debugView-stateLabelBackground: rgba(136, 136, 136, 0.27); + --vscode-debugView-valueChangedHighlight: #569cd6; + --vscode-debugConsole-infoForeground: #75beff; + --vscode-debugConsole-warningForeground: #cca700; + --vscode-debugConsole-errorForeground: #f48771; + --vscode-debugConsole-sourceForeground: #cccccc; + --vscode-debugConsoleInputIcon-foreground: #cccccc; + --vscode-debugIcon-pauseForeground: #75beff; + --vscode-debugIcon-stopForeground: #f48771; + --vscode-debugIcon-disconnectForeground: #f48771; + --vscode-debugIcon-restartForeground: #89d185; + --vscode-debugIcon-stepOverForeground: #75beff; + --vscode-debugIcon-stepIntoForeground: #75beff; + --vscode-debugIcon-stepOutForeground: #75beff; + --vscode-debugIcon-continueForeground: #75beff; + --vscode-debugIcon-stepBackForeground: #75beff; + --vscode-gitDecoration-addedResourceForeground: #81b88b; + --vscode-gitDecoration-modifiedResourceForeground: #e2c08d; + --vscode-gitDecoration-deletedResourceForeground: #c74e39; + --vscode-gitDecoration-renamedResourceForeground: #73c991; + --vscode-gitDecoration-untrackedResourceForeground: #73c991; + --vscode-gitDecoration-ignoredResourceForeground: #8c8c8c; + --vscode-gitDecoration-stageModifiedResourceForeground: #e2c08d; + --vscode-gitDecoration-stageDeletedResourceForeground: #c74e39; + --vscode-gitDecoration-conflictingResourceForeground: #e4676b; + --vscode-gitDecoration-submoduleResourceForeground: #8db9e2; + --vscode-bookmarks-lineBackground: rgba(0, 0, 0, 0); + --vscode-bookmarks-lineBorder: rgba(0, 0, 0, 0); + --vscode-bookmarks-overviewRuler: rgba(21, 126, 251, 0.53); + --vscode-gitlens-gutterBackgroundColor: rgba(255, 255, 255, 0.07); + --vscode-gitlens-gutterForegroundColor: #bebebe; + --vscode-gitlens-gutterUncommittedForegroundColor: rgba(0, 188, 242, 0.6); + --vscode-gitlens-trailingLineBackgroundColor: rgba(0, 0, 0, 0); + --vscode-gitlens-trailingLineForegroundColor: rgba(153, 153, 153, 0.35); + --vscode-gitlens-lineHighlightBackgroundColor: rgba(0, 188, 242, 0.2); + --vscode-gitlens-lineHighlightOverviewRulerColor: rgba(0, 188, 242, 0.6); + --vscode-gitlens-closedPullRequestIconColor: #f85149; + --vscode-gitlens-openPullRequestIconColor: #56d364; + --vscode-gitlens-mergedPullRequestIconColor: #995dff; + --vscode-gitlens-unpushlishedChangesIconColor: #35b15e; + --vscode-gitlens-unpublishedCommitIconColor: #35b15e; + --vscode-gitlens-unpulledChangesIconColor: #b15e35; + --vscode-gitlens-decorations\.addedForegroundColor: #81b88b; + --vscode-gitlens-decorations\.copiedForegroundColor: #73c991; + --vscode-gitlens-decorations\.deletedForegroundColor: #c74e39; + --vscode-gitlens-decorations\.ignoredForegroundColor: #8c8c8c; + --vscode-gitlens-decorations\.modifiedForegroundColor: #e2c08d; + --vscode-gitlens-decorations\.untrackedForegroundColor: #73c991; + --vscode-gitlens-decorations\.renamedForegroundColor: #73c991; + --vscode-gitlens-decorations\.branchAheadForegroundColor: #35b15e; + --vscode-gitlens-decorations\.branchBehindForegroundColor: #b15e35; + --vscode-gitlens-decorations\.branchDivergedForegroundColor: #d8af1b; + --vscode-gitlens-decorations\.branchUnpublishedForegroundColor: #35b15e; + --vscode-gitlens-decorations\.branchMissingUpstreamForegroundColor: #c74e39; +} diff --git a/html/httpgd/webviewMessages.d.ts b/html/httpgd/webviewMessages.d.ts new file mode 100644 index 000000000..728f482a5 --- /dev/null +++ b/html/httpgd/webviewMessages.d.ts @@ -0,0 +1,66 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ + +interface VsCode { + postMessage: (msg: OutMessage) => void; + setState: (state: string) => void; +} + + +interface IMessage { + message: string; +} +interface ResizeMessage extends IMessage { + message: 'resize', + height: number, + width: number, + userTriggered: boolean +} +interface LogMessage extends IMessage { + message: 'log', + body: any +} + +type OutMessage = ResizeMessage | LogMessage; + + + + +interface UpdatePlotMessage extends IMessage { + message: 'updatePlot', + svg: string, + plotId: string +} + +interface FocusPlotMessage extends IMessage { + message: 'focusPlot', + plotId: string +} + +interface ToggleStyleMessage extends IMessage { + message: 'toggleStyle', + useOverwrites: boolean +} + +interface ToggleFullWindowMessage extends IMessage { + message: 'toggleFullWindow', + useFullWindow: boolean +} + +type PreviewPlotLayout = 'multirow' | 'scroll' | 'hidden'; +interface PreviewPlotLayoutMessage extends IMessage { + message: 'togglePreviewPlotLayout', + style: PreviewPlotLayout +} + +interface HidePlotMessage extends IMessage { + message: 'hidePlot', + plotId: string +} + +interface AddPlotMessage extends IMessage { + message: 'addPlot', + html: string +} + +type InMessage = UpdatePlotMessage | FocusPlotMessage | ToggleStyleMessage | HidePlotMessage | AddPlotMessage | PreviewPlotLayoutMessage | ToggleFullWindowMessage; + diff --git a/html/session/webview/observer.js b/html/session/webview/observer.js new file mode 100644 index 000000000..1f48a8357 --- /dev/null +++ b/html/session/webview/observer.js @@ -0,0 +1,41 @@ +const MutationObserver = window.MutationObserver || window.WebKitMutationObserver; +const replaceReg = /vscode-webview:\/\//; +const testReg = /vscode-webview:\/\/.*\.[A-Za-z/0-9_-]*?\/.+/; +const watchedTags = [ + "IMG", + "A", + "LINK", + "SCRIPT" +]; +const mutationQueue = []; + +const observer = new MutationObserver(mutations => { + if (!mutationQueue.length) { + requestAnimationFrame(setSrc); + } + mutationQueue.push(mutations); +}); + +observer.observe(document.getElementById("webview-content"), { + subtree: true, + attributes: true, + attributeFilter: ["src", "href", "style", "class"], + characterData: false +}); + +function setSrc() { + for (const mutations of mutationQueue) { + const targ = mutations[0].target; + if (watchedTags.includes(targ.tagName) && testReg.test(targ.src)) { + const newSrc = targ.src.replace(replaceReg, 'https://'); + console.log( + `%c[VSC-R] %cThe file request '${targ.src}' was converted to the URL '${newSrc}'. Reason: the request appears to refer to a URL, not a local file as suggested by the file scheme. %cIf you believe this to be in error, please log an issue on GitHub.`, + "color: orange", + "color: inherit", + "font-style: italic" + ); + targ.src = newSrc; + } + } + mutationQueue.length = 0; +} diff --git a/images/DownloadFile.ico b/images/DownloadFile.ico deleted file mode 100755 index 7cfc8a82a..000000000 Binary files a/images/DownloadFile.ico and /dev/null differ diff --git a/images/DownloadFile_inverse.ico b/images/DownloadFile_inverse.ico deleted file mode 100755 index c45c4df56..000000000 Binary files a/images/DownloadFile_inverse.ico and /dev/null differ diff --git a/images/FileDownload.png b/images/FileDownload.png deleted file mode 100755 index 80e852501..000000000 Binary files a/images/FileDownload.png and /dev/null differ diff --git a/images/RStatusBarItem.png b/images/RStatusBarItem.png new file mode 100644 index 000000000..3487be38e Binary files /dev/null and b/images/RStatusBarItem.png differ diff --git a/images/icons/dark/globe.svg b/images/icons/dark/globe.svg new file mode 100644 index 000000000..1ff60a534 --- /dev/null +++ b/images/icons/dark/globe.svg @@ -0,0 +1,3 @@ + + + diff --git a/images/icons/dark/graph.svg b/images/icons/dark/graph.svg new file mode 100644 index 000000000..e07277064 --- /dev/null +++ b/images/icons/dark/graph.svg @@ -0,0 +1,3 @@ + + + diff --git a/images/icons/dark/help.svg b/images/icons/dark/help.svg new file mode 100644 index 000000000..a0a00f005 --- /dev/null +++ b/images/icons/dark/help.svg @@ -0,0 +1,3 @@ + + + diff --git a/images/icons/dark/open-preview.svg b/images/icons/dark/open-preview.svg new file mode 100644 index 000000000..b4ba465da --- /dev/null +++ b/images/icons/dark/open-preview.svg @@ -0,0 +1,3 @@ + + + diff --git a/images/icons/dark/preview.svg b/images/icons/dark/preview.svg new file mode 100644 index 000000000..6f3c492bc --- /dev/null +++ b/images/icons/dark/preview.svg @@ -0,0 +1,3 @@ + + + diff --git a/images/icons/light/globe.svg b/images/icons/light/globe.svg new file mode 100644 index 000000000..a7ad58fe5 --- /dev/null +++ b/images/icons/light/globe.svg @@ -0,0 +1,3 @@ + + + diff --git a/images/icons/light/graph.svg b/images/icons/light/graph.svg new file mode 100644 index 000000000..05facbc1f --- /dev/null +++ b/images/icons/light/graph.svg @@ -0,0 +1,3 @@ + + + diff --git a/images/icons/light/help.svg b/images/icons/light/help.svg new file mode 100644 index 000000000..a6977ef94 --- /dev/null +++ b/images/icons/light/help.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/images/icons/light/open-preview.svg b/images/icons/light/open-preview.svg new file mode 100644 index 000000000..0e0e63c62 --- /dev/null +++ b/images/icons/light/open-preview.svg @@ -0,0 +1,3 @@ + + + diff --git a/images/icons/light/preview.svg b/images/icons/light/preview.svg new file mode 100644 index 000000000..2263883c6 --- /dev/null +++ b/images/icons/light/preview.svg @@ -0,0 +1,3 @@ + + + diff --git a/images/vscode_addins.png b/images/vscode_addins.png new file mode 100644 index 000000000..64a6f8c9f Binary files /dev/null and b/images/vscode_addins.png differ diff --git a/language-configuration/dcf.json b/language-configuration/dcf.json new file mode 100644 index 000000000..6badf05d0 --- /dev/null +++ b/language-configuration/dcf.json @@ -0,0 +1,24 @@ +{ + "surroundingPairs": [ + ["(", ")"], + ["{", "}"], + ["\"", "\""], + ["[", "]"], + ["'", "'"] + ], + "brackets": [ + ["[", "]"], + ["(", ")"], + ["{", "}"] + ], + "autoClosingPairs": [ + ["{", "}"], + ["(", ")"], + ["[", "]"], + ["\"", "\""], + ["'", "'"] + ], + "comments": { + "lineComment": "#" + } +} \ No newline at end of file diff --git a/language-configuration/rbuildignore.json b/language-configuration/rbuildignore.json new file mode 100644 index 000000000..7a73a41bf --- /dev/null +++ b/language-configuration/rbuildignore.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/rd-configuration.json b/language-configuration/rd.json similarity index 87% rename from rd-configuration.json rename to language-configuration/rd.json index b69211ee6..0910d1ef9 100644 --- a/rd-configuration.json +++ b/language-configuration/rd.json @@ -12,7 +12,8 @@ ["[", "]"], ["(", ")"], ["\"", "\""], - ["'", "'"] + ["'", "'"], + ["`", "`"] ], "surroundingPairs": [ ["{", "}"], @@ -20,6 +21,7 @@ ["(", ")"], ["\"", "\""], ["'", "'"], + ["`", "`"], ["$", "$"] ] } \ No newline at end of file diff --git a/r-configuration.json b/language-configuration/rnamespace.json similarity index 77% rename from r-configuration.json rename to language-configuration/rnamespace.json index 6c6e98410..de5fe4b0b 100644 --- a/r-configuration.json +++ b/language-configuration/rnamespace.json @@ -8,16 +8,21 @@ ["(", ")"] ], "autoClosingPairs": [ + ["#'", ""], ["{", "}"], ["[", "]"], ["(", ")"], - ["\"", "\""] + ["\"", "\""], + ["'", "'"], + ["`", "`"] ], "surroundingPairs": [ ["{", "}"], ["[", "]"], ["(", ")"], - ["\"", "\""] + ["\"", "\""], + ["'", "'"], + ["`", "`"] ], "folding": { "markers": { diff --git a/package-lock.json b/package-lock.json index d63931027..5f82bb2f3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6961 +1,5526 @@ { "name": "r", - "version": "1.1.5", - "lockfileVersion": 1, + "version": "2.8.7", + "lockfileVersion": 3, "requires": true, - "dependencies": { - "@babel/code-frame": { - "version": "7.5.5", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.5.5.tgz", - "integrity": "sha512-27d4lZoomVyo51VegxI20xZPuSHusqbQag/ztrBC7wegWoQ1nLREPVSKSW8byhTlzTKyNE4ifaTA6lCp7JjpFw==", - "dev": true, - "requires": { - "@babel/highlight": "^7.0.0" + "packages": { + "": { + "name": "r", + "version": "2.8.7", + "license": "SEE LICENSE IN LICENSE", + "dependencies": { + "ag-grid-community": "^31.3.2", + "cheerio": "1.0.0-rc.12", + "crypto": "^1.0.1", + "ejs": "^3.1.10", + "fs-extra": "^10.0.0", + "highlight.js": "^11.9.0", + "httpgd": "0.1.6", + "jquery": "^3.7.1", + "jquery.json-viewer": "^1.5.0", + "js-yaml": "^4.1.0", + "node-fetch": "^2.6.7", + "vscode-languageclient": "^9.0.1", + "vsls": "^1.0.4753", + "winreg": "^1.2.4" + }, + "devDependencies": { + "@types/cheerio": "^0.22.29", + "@types/ejs": "^3.0.6", + "@types/express": "^4.17.12", + "@types/fs-extra": "^9.0.11", + "@types/glob": "^8.0.0", + "@types/highlight.js": "^10.1.0", + "@types/js-yaml": "^4.0.2", + "@types/mocha": "^8.2.2", + "@types/node": "^18.17.1", + "@types/node-fetch": "^2.5.10", + "@types/sinon": "^10.0.13", + "@types/vscode": "^1.75.0", + "@types/winreg": "^1.2.31", + "@typescript-eslint/eslint-plugin": "^5.30.0", + "@typescript-eslint/parser": "^5.30.0", + "@vscode/test-electron": "^2.2.3", + "esbuild": "^0.27.3", + "eslint": "^7.28.0", + "eslint-plugin-jsdoc": "^35.1.3", + "git-cliff": "^2.12.0", + "mocha": "^11.1.0", + "sinon": "^15.0.1", + "typescript": "^4.7.2" + }, + "engines": { + "vscode": "^1.75.0" } }, - "@babel/highlight": { - "version": "7.5.0", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.5.0.tgz", - "integrity": "sha512-7dV4eu9gBxoM0dAnj/BCFDW9LFU0zvTrkq0ugM7pnHEgguOEeOz1so2ZghEdzviYzQEED0r4EAgpsBChKy1TRQ==", + "node_modules/@babel/code-frame": { + "version": "7.12.11", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.11.tgz", + "integrity": "sha512-Zt1yodBx1UcyiePMSkWnU4hPqhwq7hGi2nFL1LeA3EUl+q2LQx16MISgJ0+z7dnmgvP9QtIleuETGOiOH1RcIw==", "dev": true, - "requires": { - "chalk": "^2.0.0", - "esutils": "^2.0.2", - "js-tokens": "^4.0.0" + "license": "MIT", + "dependencies": { + "@babel/highlight": "^7.10.4" } }, - "@types/fs-extra": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-8.0.0.tgz", - "integrity": "sha512-bCtL5v9zdbQW86yexOlXWTEGvLNqWxMFyi7gQA7Gcthbezr2cPSOb8SkESVKA937QD5cIwOFLDFt0MQoXOEr9Q==", + "node_modules/@babel/helper-validator-identifier": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", + "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", "dev": true, - "requires": { - "@types/node": "*" + "license": "MIT", + "engines": { + "node": ">=6.9.0" } }, - "@types/mocha": { - "version": "5.2.7", - "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-5.2.7.tgz", - "integrity": "sha512-NYrtPht0wGzhwe9+/idPaBB+TqkY9AhTvOLMkThm0IoEfLaiVQZwBwyJ5puCkO3AUCWrmcoePjp2mbFocKy4SQ==", - "dev": true - }, - "@types/node": { - "version": "12.7.8", - "resolved": "https://registry.npmjs.org/@types/node/-/node-12.7.8.tgz", - "integrity": "sha512-FMdVn84tJJdV+xe+53sYiZS4R5yn1mAIxfj+DVoNiQjTYz1+OYmjwEZr1ev9nU0axXwda0QDbYl06QHanRVH3A==", - "dev": true - }, - "@webassemblyjs/ast": { - "version": "1.8.5", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.8.5.tgz", - "integrity": "sha512-aJMfngIZ65+t71C3y2nBBg5FFG0Okt9m0XEgWZ7Ywgn1oMAT8cNwx00Uv1cQyHtidq0Xn94R4TAywO+LCQ+ZAQ==", + "node_modules/@babel/highlight": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.25.9.tgz", + "integrity": "sha512-llL88JShoCsth8fF8R4SJnIn+WLvR6ccFxu1H3FlMhDontdcmZWf2HgIZ7AIqV3Xcck1idlohrN4EUBQz6klbw==", "dev": true, - "requires": { - "@webassemblyjs/helper-module-context": "1.8.5", - "@webassemblyjs/helper-wasm-bytecode": "1.8.5", - "@webassemblyjs/wast-parser": "1.8.5" - } - }, - "@webassemblyjs/floating-point-hex-parser": { - "version": "1.8.5", - "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.8.5.tgz", - "integrity": "sha512-9p+79WHru1oqBh9ewP9zW95E3XAo+90oth7S5Re3eQnECGq59ly1Ri5tsIipKGpiStHsUYmY3zMLqtk3gTcOtQ==", - "dev": true - }, - "@webassemblyjs/helper-api-error": { - "version": "1.8.5", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.8.5.tgz", - "integrity": "sha512-Za/tnzsvnqdaSPOUXHyKJ2XI7PDX64kWtURyGiJJZKVEdFOsdKUCPTNEVFZq3zJ2R0G5wc2PZ5gvdTRFgm81zA==", - "dev": true - }, - "@webassemblyjs/helper-buffer": { - "version": "1.8.5", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.8.5.tgz", - "integrity": "sha512-Ri2R8nOS0U6G49Q86goFIPNgjyl6+oE1abW1pS84BuhP1Qcr5JqMwRFT3Ah3ADDDYGEgGs1iyb1DGX+kAi/c/Q==", - "dev": true - }, - "@webassemblyjs/helper-code-frame": { - "version": "1.8.5", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-code-frame/-/helper-code-frame-1.8.5.tgz", - "integrity": "sha512-VQAadSubZIhNpH46IR3yWO4kZZjMxN1opDrzePLdVKAZ+DFjkGD/rf4v1jap744uPVU6yjL/smZbRIIJTOUnKQ==", - "dev": true, - "requires": { - "@webassemblyjs/wast-printer": "1.8.5" - } - }, - "@webassemblyjs/helper-fsm": { - "version": "1.8.5", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-fsm/-/helper-fsm-1.8.5.tgz", - "integrity": "sha512-kRuX/saORcg8se/ft6Q2UbRpZwP4y7YrWsLXPbbmtepKr22i8Z4O3V5QE9DbZK908dh5Xya4Un57SDIKwB9eow==", - "dev": true - }, - "@webassemblyjs/helper-module-context": { - "version": "1.8.5", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-module-context/-/helper-module-context-1.8.5.tgz", - "integrity": "sha512-/O1B236mN7UNEU4t9X7Pj38i4VoU8CcMHyy3l2cV/kIF4U5KoHXDVqcDuOs1ltkac90IM4vZdHc52t1x8Yfs3g==", - "dev": true, - "requires": { - "@webassemblyjs/ast": "1.8.5", - "mamacro": "^0.0.3" - } - }, - "@webassemblyjs/helper-wasm-bytecode": { - "version": "1.8.5", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.8.5.tgz", - "integrity": "sha512-Cu4YMYG3Ddl72CbmpjU/wbP6SACcOPVbHN1dI4VJNJVgFwaKf1ppeFJrwydOG3NDHxVGuCfPlLZNyEdIYlQ6QQ==", - "dev": true - }, - "@webassemblyjs/helper-wasm-section": { - "version": "1.8.5", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.8.5.tgz", - "integrity": "sha512-VV083zwR+VTrIWWtgIUpqfvVdK4ff38loRmrdDBgBT8ADXYsEZ5mPQ4Nde90N3UYatHdYoDIFb7oHzMncI02tA==", - "dev": true, - "requires": { - "@webassemblyjs/ast": "1.8.5", - "@webassemblyjs/helper-buffer": "1.8.5", - "@webassemblyjs/helper-wasm-bytecode": "1.8.5", - "@webassemblyjs/wasm-gen": "1.8.5" - } - }, - "@webassemblyjs/ieee754": { - "version": "1.8.5", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.8.5.tgz", - "integrity": "sha512-aaCvQYrvKbY/n6wKHb/ylAJr27GglahUO89CcGXMItrOBqRarUMxWLJgxm9PJNuKULwN5n1csT9bYoMeZOGF3g==", - "dev": true, - "requires": { - "@xtuc/ieee754": "^1.2.0" - } - }, - "@webassemblyjs/leb128": { - "version": "1.8.5", - "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.8.5.tgz", - "integrity": "sha512-plYUuUwleLIziknvlP8VpTgO4kqNaH57Y3JnNa6DLpu/sGcP6hbVdfdX5aHAV716pQBKrfuU26BJK29qY37J7A==", - "dev": true, - "requires": { - "@xtuc/long": "4.2.2" - } - }, - "@webassemblyjs/utf8": { - "version": "1.8.5", - "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.8.5.tgz", - "integrity": "sha512-U7zgftmQriw37tfD934UNInokz6yTmn29inT2cAetAsaU9YeVCveWEwhKL1Mg4yS7q//NGdzy79nlXh3bT8Kjw==", - "dev": true - }, - "@webassemblyjs/wasm-edit": { - "version": "1.8.5", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.8.5.tgz", - "integrity": "sha512-A41EMy8MWw5yvqj7MQzkDjU29K7UJq1VrX2vWLzfpRHt3ISftOXqrtojn7nlPsZ9Ijhp5NwuODuycSvfAO/26Q==", - "dev": true, - "requires": { - "@webassemblyjs/ast": "1.8.5", - "@webassemblyjs/helper-buffer": "1.8.5", - "@webassemblyjs/helper-wasm-bytecode": "1.8.5", - "@webassemblyjs/helper-wasm-section": "1.8.5", - "@webassemblyjs/wasm-gen": "1.8.5", - "@webassemblyjs/wasm-opt": "1.8.5", - "@webassemblyjs/wasm-parser": "1.8.5", - "@webassemblyjs/wast-printer": "1.8.5" - } - }, - "@webassemblyjs/wasm-gen": { - "version": "1.8.5", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.8.5.tgz", - "integrity": "sha512-BCZBT0LURC0CXDzj5FXSc2FPTsxwp3nWcqXQdOZE4U7h7i8FqtFK5Egia6f9raQLpEKT1VL7zr4r3+QX6zArWg==", - "dev": true, - "requires": { - "@webassemblyjs/ast": "1.8.5", - "@webassemblyjs/helper-wasm-bytecode": "1.8.5", - "@webassemblyjs/ieee754": "1.8.5", - "@webassemblyjs/leb128": "1.8.5", - "@webassemblyjs/utf8": "1.8.5" - } - }, - "@webassemblyjs/wasm-opt": { - "version": "1.8.5", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.8.5.tgz", - "integrity": "sha512-HKo2mO/Uh9A6ojzu7cjslGaHaUU14LdLbGEKqTR7PBKwT6LdPtLLh9fPY33rmr5wcOMrsWDbbdCHq4hQUdd37Q==", - "dev": true, - "requires": { - "@webassemblyjs/ast": "1.8.5", - "@webassemblyjs/helper-buffer": "1.8.5", - "@webassemblyjs/wasm-gen": "1.8.5", - "@webassemblyjs/wasm-parser": "1.8.5" + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.25.9", + "chalk": "^2.4.2", + "js-tokens": "^4.0.0", + "picocolors": "^1.0.0" + }, + "engines": { + "node": ">=6.9.0" } }, - "@webassemblyjs/wasm-parser": { - "version": "1.8.5", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.8.5.tgz", - "integrity": "sha512-pi0SYE9T6tfcMkthwcgCpL0cM9nRYr6/6fjgDtL6q/ZqKHdMWvxitRi5JcZ7RI4SNJJYnYNaWy5UUrHQy998lw==", - "dev": true, - "requires": { - "@webassemblyjs/ast": "1.8.5", - "@webassemblyjs/helper-api-error": "1.8.5", - "@webassemblyjs/helper-wasm-bytecode": "1.8.5", - "@webassemblyjs/ieee754": "1.8.5", - "@webassemblyjs/leb128": "1.8.5", - "@webassemblyjs/utf8": "1.8.5" - } - }, - "@webassemblyjs/wast-parser": { - "version": "1.8.5", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-parser/-/wast-parser-1.8.5.tgz", - "integrity": "sha512-daXC1FyKWHF1i11obK086QRlsMsY4+tIOKgBqI1lxAnkp9xe9YMcgOxm9kLe+ttjs5aWV2KKE1TWJCN57/Btsg==", - "dev": true, - "requires": { - "@webassemblyjs/ast": "1.8.5", - "@webassemblyjs/floating-point-hex-parser": "1.8.5", - "@webassemblyjs/helper-api-error": "1.8.5", - "@webassemblyjs/helper-code-frame": "1.8.5", - "@webassemblyjs/helper-fsm": "1.8.5", - "@xtuc/long": "4.2.2" + "node_modules/@babel/highlight/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" } }, - "@webassemblyjs/wast-printer": { - "version": "1.8.5", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.8.5.tgz", - "integrity": "sha512-w0U0pD4EhlnvRyeJzBqaVSJAo9w/ce7/WPogeXLzGkO6hzhr4GnQIZ4W4uUt5b9ooAaXPtnXlj0gzsXEOUNYMg==", + "node_modules/@babel/highlight/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", "dev": true, - "requires": { - "@webassemblyjs/ast": "1.8.5", - "@webassemblyjs/wast-parser": "1.8.5", - "@xtuc/long": "4.2.2" + "license": "MIT", + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" } }, - "@xtuc/ieee754": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", - "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==", - "dev": true - }, - "@xtuc/long": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", - "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", - "dev": true + "node_modules/@babel/highlight/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "1.1.3" + } }, - "acorn": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.3.0.tgz", - "integrity": "sha512-/czfa8BwS88b9gWQVhc8eknunSA2DoJpJyTQkhheIf5E48u1N0R4q/YxxsAeqRrmK9TQ/uYfgLDfZo91UlANIA==", - "dev": true + "node_modules/@babel/highlight/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true, + "license": "MIT" }, - "agent-base": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-4.2.1.tgz", - "integrity": "sha512-JVwXMr9nHYTUXsBFKUqhJwvlcYU/blreOEUkhNR2eXZIvwd+c+o5V4MgDPKWnMS/56awN3TRzIP+KoPn+roQtg==", + "node_modules/@babel/highlight/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", "dev": true, - "requires": { - "es6-promisify": "^5.0.0" + "license": "MIT", + "engines": { + "node": ">=0.8.0" } }, - "ajv": { - "version": "6.10.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.10.0.tgz", - "integrity": "sha512-nffhOpkymDECQyR0mnsUtoCE8RlX38G0rYP+wgLWFyZuUyuuojSSvi/+euOiQBIn63whYwYVIIH1TvE3tu4OEg==", + "node_modules/@babel/highlight/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", "dev": true, - "requires": { - "fast-deep-equal": "^2.0.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" + "license": "MIT", + "engines": { + "node": ">=4" } }, - "ajv-errors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/ajv-errors/-/ajv-errors-1.0.1.tgz", - "integrity": "sha512-DCRfO/4nQ+89p/RK43i8Ezd41EqdGIU4ld7nGF8OQ14oc/we5rEntLCUa7+jrn3nn83BosfwZA0wb4pon2o8iQ==", - "dev": true - }, - "ajv-keywords": { - "version": "3.4.1", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.4.1.tgz", - "integrity": "sha512-RO1ibKvd27e6FEShVFfPALuHI3WjSVNeK5FIsmme/LYRNxjKuNj+Dt7bucLa6NdSv3JcVTyMlm9kGR84z1XpaQ==", - "dev": true + "node_modules/@babel/highlight/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } }, - "ansi-align": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-2.0.0.tgz", - "integrity": "sha1-w2rsy6VjuJzrVW82kPCx2eNUf38=", + "node_modules/@es-joy/jsdoccomment": { + "version": "0.9.0-alpha.1", + "resolved": "https://registry.npmjs.org/@es-joy/jsdoccomment/-/jsdoccomment-0.9.0-alpha.1.tgz", + "integrity": "sha512-Clxxc0PwpISoYYBibA+1L2qFJ7gvFVhI2Hos87S06K+Q0cXdOhZQJNKWuaQGPAeHjZEuUB/YoWOfwjuF2wirqA==", "dev": true, - "requires": { - "string-width": "^2.0.0" + "license": "MIT", + "dependencies": { + "comment-parser": "1.1.6-beta.0", + "esquery": "^1.4.0", + "jsdoc-type-pratt-parser": "1.0.4" + }, + "engines": { + "node": ">=12.0.0" } }, - "ansi-colors": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-3.2.3.tgz", - "integrity": "sha512-LEHHyuhlPY3TmuUYMh2oz89lTShfvgbmzaBcxve9t/9Wuy7Dwf4yoAKcND7KFT1HAQfqZ12qtc+DUrBMeKF9nw==", - "dev": true + "node_modules/@es-joy/jsdoccomment/node_modules/jsdoc-type-pratt-parser": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/jsdoc-type-pratt-parser/-/jsdoc-type-pratt-parser-1.0.4.tgz", + "integrity": "sha512-jzmW9gokeq9+bHPDR1nCeidMyFUikdZlbOhKzh9+/nJqB75XhpNKec1/UuxW5c4+O+Pi31Gc/dCboyfSm/pSpQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + } }, - "ansi-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", - "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", - "dev": true + "node_modules/@esbuild/aix-ppc64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.4.tgz", + "integrity": "sha512-cQPwL2mp2nSmHHJlCyoXgHGhbEPMrEEU5xhkcy3Hs/O7nGZqEpZ2sUtLaL9MORLtDfRvVl2/3PAuEkYZH0Ty8Q==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.4.tgz", + "integrity": "sha512-X9bUgvxiC8CHAGKYufLIHGXPJWnr0OCdR0anD2e21vdvgCI8lIfqFbnoeOz7lBjdrAGUhqLZLcQo6MLhTO2DKQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.4.tgz", + "integrity": "sha512-gdLscB7v75wRfu7QSm/zg6Rx29VLdy9eTr2t44sfTW7CxwAtQghZ4ZnqHk3/ogz7xao0QAgrkradbBzcqFPasw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.4.tgz", + "integrity": "sha512-PzPFnBNVF292sfpfhiyiXCGSn9HZg5BcAz+ivBuSsl6Rk4ga1oEXAamhOXRFyMcjwr2DVtm40G65N3GLeH1Lvw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.4.tgz", + "integrity": "sha512-b7xaGIwdJlht8ZFCvMkpDN6uiSmnxxK56N2GDTMYPr2/gzvfdQN8rTfBsvVKmIVY/X7EM+/hJKEIbbHs9oA4tQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.4.tgz", + "integrity": "sha512-sR+OiKLwd15nmCdqpXMnuJ9W2kpy0KigzqScqHI3Hqwr7IXxBp3Yva+yJwoqh7rE8V77tdoheRYataNKL4QrPw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.4.tgz", + "integrity": "sha512-jnfpKe+p79tCnm4GVav68A7tUFeKQwQyLgESwEAUzyxk/TJr4QdGog9sqWNcUbr/bZt/O/HXouspuQDd9JxFSw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.4.tgz", + "integrity": "sha512-2kb4ceA/CpfUrIcTUl1wrP/9ad9Atrp5J94Lq69w7UwOMolPIGrfLSvAKJp0RTvkPPyn6CIWrNy13kyLikZRZQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.4.tgz", + "integrity": "sha512-aBYgcIxX/wd5n2ys0yESGeYMGF+pv6g0DhZr3G1ZG4jMfruU9Tl1i2Z+Wnj9/KjGz1lTLCcorqE2viePZqj4Eg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.4.tgz", + "integrity": "sha512-7nQOttdzVGth1iz57kxg9uCz57dxQLHWxopL6mYuYthohPKEK0vU0C3O21CcBK6KDlkYVcnDXY099HcCDXd9dA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.4.tgz", + "integrity": "sha512-oPtixtAIzgvzYcKBQM/qZ3R+9TEUd1aNJQu0HhGyqtx6oS7qTpvjheIWBbes4+qu1bNlo2V4cbkISr8q6gRBFA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.4.tgz", + "integrity": "sha512-8mL/vh8qeCoRcFH2nM8wm5uJP+ZcVYGGayMavi8GmRJjuI3g1v6Z7Ni0JJKAJW+m0EtUuARb6Lmp4hMjzCBWzA==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.4.tgz", + "integrity": "sha512-1RdrWFFiiLIW7LQq9Q2NES+HiD4NyT8Itj9AUeCl0IVCA459WnPhREKgwrpaIfTOe+/2rdntisegiPWn/r/aAw==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.4.tgz", + "integrity": "sha512-tLCwNG47l3sd9lpfyx9LAGEGItCUeRCWeAx6x2Jmbav65nAwoPXfewtAdtbtit/pJFLUWOhpv0FpS6GQAmPrHA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.4.tgz", + "integrity": "sha512-BnASypppbUWyqjd1KIpU4AUBiIhVr6YlHx/cnPgqEkNoVOhHg+YiSVxM1RLfiy4t9cAulbRGTNCKOcqHrEQLIw==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.4.tgz", + "integrity": "sha512-+eUqgb/Z7vxVLezG8bVB9SfBie89gMueS+I0xYh2tJdw3vqA/0ImZJ2ROeWwVJN59ihBeZ7Tu92dF/5dy5FttA==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.4.tgz", + "integrity": "sha512-S5qOXrKV8BQEzJPVxAwnryi2+Iq5pB40gTEIT69BQONqR7JH1EPIcQ/Uiv9mCnn05jff9umq/5nqzxlqTOg9NA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.4.tgz", + "integrity": "sha512-xHT8X4sb0GS8qTqiwzHqpY00C95DPAq7nAwX35Ie/s+LO9830hrMd3oX0ZMKLvy7vsonee73x0lmcdOVXFzd6Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.4.tgz", + "integrity": "sha512-RugOvOdXfdyi5Tyv40kgQnI0byv66BFgAqjdgtAKqHoZTbTF2QqfQrFwa7cHEORJf6X2ht+l9ABLMP0dnKYsgg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.4.tgz", + "integrity": "sha512-2MyL3IAaTX+1/qP0O1SwskwcwCoOI4kV2IBX1xYnDDqthmq5ArrW94qSIKCAuRraMgPOmG0RDTA74mzYNQA9ow==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.4.tgz", + "integrity": "sha512-u8fg/jQ5aQDfsnIV6+KwLOf1CmJnfu1ShpwqdwC0uA7ZPwFws55Ngc12vBdeUdnuWoQYx/SOQLGDcdlfXhYmXQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.4.tgz", + "integrity": "sha512-JkTZrl6VbyO8lDQO3yv26nNr2RM2yZzNrNHEsj9bm6dOwwu9OYN28CjzZkH57bh4w0I2F7IodpQvUAEd1mbWXg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.4.tgz", + "integrity": "sha512-/gOzgaewZJfeJTlsWhvUEmUG4tWEY2Spp5M20INYRg2ZKl9QPO3QEEgPeRtLjEWSW8FilRNacPOg8R1uaYkA6g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.4.tgz", + "integrity": "sha512-Z9SExBg2y32smoDQdf1HRwHRt6vAHLXcxD2uGgO/v2jK7Y718Ix4ndsbNMU/+1Qiem9OiOdaqitioZwxivhXYg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.4.tgz", + "integrity": "sha512-DAyGLS0Jz5G5iixEbMHi5KdiApqHBWMGzTtMiJ72ZOLhbu/bzxgAe8Ue8CTS3n3HbIUHQz/L51yMdGMeoxXNJw==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.4.tgz", + "integrity": "sha512-+knoa0BDoeXgkNvvV1vvbZX4+hizelrkwmGJBdT17t8FNPwG2lKemmuMZlmaNQ3ws3DKKCxpb4zRZEIp3UxFCg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } }, - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "node_modules/@eslint-community/eslint-utils": { + "version": "4.9.1", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz", + "integrity": "sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==", "dev": true, - "requires": { - "color-convert": "^1.9.0" + "license": "MIT", + "dependencies": { + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" } }, - "anymatch": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz", - "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==", - "dev": true, - "requires": { - "micromatch": "^3.1.4", - "normalize-path": "^2.1.1" - }, - "dependencies": { - "braces": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", - "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", - "dev": true, - "requires": { - "arr-flatten": "^1.1.0", - "array-unique": "^0.3.2", - "extend-shallow": "^2.0.1", - "fill-range": "^4.0.0", - "isobject": "^3.0.1", - "repeat-element": "^1.1.2", - "snapdragon": "^0.8.1", - "snapdragon-node": "^2.0.1", - "split-string": "^3.0.2", - "to-regex": "^3.0.1" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "fill-range": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", - "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", - "dev": true, - "requires": { - "extend-shallow": "^2.0.1", - "is-number": "^3.0.0", - "repeat-string": "^1.6.1", - "to-regex-range": "^2.1.0" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "is-number": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", - "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "micromatch": { - "version": "3.1.10", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", - "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", - "dev": true, - "requires": { - "arr-diff": "^4.0.0", - "array-unique": "^0.3.2", - "braces": "^2.3.1", - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "extglob": "^2.0.4", - "fragment-cache": "^0.2.1", - "kind-of": "^6.0.2", - "nanomatch": "^1.2.9", - "object.pick": "^1.3.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.2" - } - }, - "normalize-path": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", - "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", - "dev": true, - "requires": { - "remove-trailing-separator": "^1.0.1" - } - }, - "to-regex-range": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", - "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", - "dev": true, - "requires": { - "is-number": "^3.0.0", - "repeat-string": "^1.6.1" - } - } + "node_modules/@eslint-community/regexpp": { + "version": "4.12.2", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz", + "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" } }, - "aproba": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", - "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==", - "dev": true + "node_modules/@eslint/eslintrc": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.4.3.tgz", + "integrity": "sha512-J6KFFz5QCYUJq3pf0mjEcCJVERbzv71PUIDczuh9JkwGEzced6CO5ADLHB1rbf/+oPBtoPfMYNOpGDzCANlbXw==", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.1.1", + "espree": "^7.3.0", + "globals": "^13.9.0", + "ignore": "^4.0.6", + "import-fresh": "^3.2.1", + "js-yaml": "^3.13.1", + "minimatch": "^3.0.4", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } }, - "argparse": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.9.tgz", - "integrity": "sha1-c9g7wmP4bpf4zE9rrhsOkKfSLIY=", + "node_modules/@eslint/eslintrc/node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", "dev": true, - "requires": { + "license": "MIT", + "dependencies": { "sprintf-js": "~1.0.2" } }, - "arr-diff": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", - "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=", - "dev": true - }, - "arr-flatten": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz", - "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==", - "dev": true - }, - "arr-union": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz", - "integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=", - "dev": true - }, - "array-unique": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", - "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", - "dev": true - }, - "asn1": { - "version": "0.2.4", - "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", - "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==", + "node_modules/@eslint/eslintrc/node_modules/ignore": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", + "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", "dev": true, - "requires": { - "safer-buffer": "~2.1.0" + "license": "MIT", + "engines": { + "node": ">= 4" } }, - "asn1.js": { - "version": "4.10.1", - "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-4.10.1.tgz", - "integrity": "sha512-p32cOF5q0Zqs9uBiONKYLm6BClCoBCM5O9JfeUSlnQLBTxYdTK+pW+nXflm8UkKd2UYlEbYz5qEi0JuZR9ckSw==", + "node_modules/@eslint/eslintrc/node_modules/js-yaml": { + "version": "3.14.2", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.2.tgz", + "integrity": "sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg==", "dev": true, - "requires": { - "bn.js": "^4.0.0", - "inherits": "^2.0.1", - "minimalistic-assert": "^1.0.0" + "license": "MIT", + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" } }, - "assert": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/assert/-/assert-1.5.0.tgz", - "integrity": "sha512-EDsgawzwoun2CZkCgtxJbv392v4nbk9XDD06zI+kQYoBM/3RBWLlEyJARDOmhAAosBjWACEkKL6S+lIZtcAubA==", + "node_modules/@humanwhocodes/config-array": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.5.0.tgz", + "integrity": "sha512-FagtKFz74XrTl7y6HCzQpwDfXP0yhxe9lHLD1UZxjvZIcbyRz8zTFF/yYNfSfzU414eDwZ1SrO0Qvtyf+wFMQg==", + "deprecated": "Use @eslint/config-array instead", "dev": true, - "requires": { - "object-assign": "^4.1.1", - "util": "0.10.3" + "license": "Apache-2.0", + "dependencies": { + "@humanwhocodes/object-schema": "^1.2.0", + "debug": "^4.1.1", + "minimatch": "^3.0.4" + }, + "engines": { + "node": ">=10.10.0" } }, - "assert-plus": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", - "dev": true - }, - "assign-symbols": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz", - "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=", - "dev": true - }, - "async-each": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.3.tgz", - "integrity": "sha512-z/WhQ5FPySLdvREByI2vZiTWwCnF0moMJ1hK9YQwDTHKh6I7/uSckMetoRGb5UBZPC1z0jlw+n/XCgjeH7y1AQ==", - "dev": true - }, - "asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", - "dev": true + "node_modules/@humanwhocodes/object-schema": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", + "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", + "deprecated": "Use @eslint/object-schema instead", + "dev": true, + "license": "BSD-3-Clause" }, - "atob": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", - "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==", - "dev": true - }, - "aws-sign2": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", - "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=", - "dev": true - }, - "aws4": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.8.0.tgz", - "integrity": "sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ==", - "dev": true - }, - "balanced-match": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", - "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", - "dev": true - }, - "base": { - "version": "0.11.2", - "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz", - "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==", - "dev": true, - "requires": { - "cache-base": "^1.0.1", - "class-utils": "^0.3.5", - "component-emitter": "^1.2.1", - "define-property": "^1.0.0", - "isobject": "^3.0.1", - "mixin-deep": "^1.2.0", - "pascalcase": "^0.1.1" - }, - "dependencies": { - "define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", - "dev": true, - "requires": { - "is-descriptor": "^1.0.0" - } - }, - "is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - } - } + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" } }, - "base64-js": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.2.0.tgz", - "integrity": "sha1-o5mS1yNYSBGYK+XikLtqU9hnAPE=", - "dev": true + "node_modules/@isaacs/cliui/node_modules/ansi-regex": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", + "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } }, - "bcrypt-pbkdf": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", - "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=", + "node_modules/@isaacs/cliui/node_modules/strip-ansi": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz", + "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==", "dev": true, - "requires": { - "tweetnacl": "^0.14.3" + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.2.2" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" } }, - "big.js": { - "version": "5.2.2", - "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", - "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==", - "dev": true - }, - "binary-extensions": { - "version": "1.13.1", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.13.1.tgz", - "integrity": "sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw==", - "dev": true - }, - "bluebird": { - "version": "3.5.5", - "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.5.tgz", - "integrity": "sha512-5am6HnnfN+urzt4yfg7IgTbotDjIT/u8AJpEt0sIU9FtXfVeezXAPKswrG+xKUCOYAINpSdgZVDU6QFh+cuH3w==", - "dev": true - }, - "bn.js": { - "version": "4.11.8", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.8.tgz", - "integrity": "sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA==", - "dev": true - }, - "boxen": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/boxen/-/boxen-1.3.0.tgz", - "integrity": "sha512-TNPjfTr432qx7yOjQyaXm3dSR0MH9vXp7eT1BFSl/C51g+EFnOR9hTg1IreahGBmDNCehscshe45f+C1TBZbLw==", - "dev": true, - "requires": { - "ansi-align": "^2.0.0", - "camelcase": "^4.0.0", - "chalk": "^2.0.1", - "cli-boxes": "^1.0.0", - "string-width": "^2.0.0", - "term-size": "^1.2.0", - "widest-line": "^2.0.0" - }, - "dependencies": { - "chalk": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.1.tgz", - "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", - "dev": true - }, - "supports-color": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.4.0.tgz", - "integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } + "node_modules/@microsoft/servicehub-framework": { + "version": "2.6.74", + "resolved": "https://registry.npmjs.org/@microsoft/servicehub-framework/-/servicehub-framework-2.6.74.tgz", + "integrity": "sha512-QJ//zzvxffupIkzupnVbMYY5YDOP+g5FlG6x0Pl7svRyq8pAouiibckJJcZlMtsMypKWwAnVBKb9/sonEOsUxw==", + "license": "LICENSE.txt", + "dependencies": { + "await-semaphore": "^0.1.3", + "msgpack-lite": "^0.1.26", + "nerdbank-streams": "2.5.60", + "strict-event-emitter-types": "^2.0.0", + "vscode-jsonrpc": "^4.0.0" } }, - "brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "requires": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" + "node_modules/@microsoft/servicehub-framework/node_modules/vscode-jsonrpc": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-4.0.0.tgz", + "integrity": "sha512-perEnXQdQOJMTDFNv+UF3h1Y0z4iSiaN9jIlb0OqIYgosPCZGYh/MCUlkFtV2668PL69lRDO32hmvL2yiidUYg==", + "license": "MIT", + "engines": { + "node": ">=8.0.0 || >=10.0.0" } }, - "braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", "dev": true, - "requires": { - "fill-range": "^7.0.1" + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" } }, - "brorand": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", - "integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=", - "dev": true - }, - "browser-stdout": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", - "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", - "dev": true - }, - "browserify-aes": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz", - "integrity": "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==", + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", "dev": true, - "requires": { - "buffer-xor": "^1.0.3", - "cipher-base": "^1.0.0", - "create-hash": "^1.1.0", - "evp_bytestokey": "^1.0.3", - "inherits": "^2.0.1", - "safe-buffer": "^5.0.1" + "license": "MIT", + "engines": { + "node": ">= 8" } }, - "browserify-cipher": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/browserify-cipher/-/browserify-cipher-1.0.1.tgz", - "integrity": "sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w==", + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", "dev": true, - "requires": { - "browserify-aes": "^1.0.4", - "browserify-des": "^1.0.0", - "evp_bytestokey": "^1.0.0" + "license": "MIT", + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" } }, - "browserify-des": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/browserify-des/-/browserify-des-1.0.2.tgz", - "integrity": "sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A==", + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", "dev": true, - "requires": { - "cipher-base": "^1.0.1", - "des.js": "^1.0.0", - "inherits": "^2.0.1", - "safe-buffer": "^5.1.2" + "license": "MIT", + "optional": true, + "engines": { + "node": ">=14" } }, - "browserify-rsa": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.0.1.tgz", - "integrity": "sha1-IeCr+vbyApzy+vsTNWenAdQTVSQ=", + "node_modules/@sec-ant/readable-stream": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@sec-ant/readable-stream/-/readable-stream-0.4.1.tgz", + "integrity": "sha512-831qok9r2t8AlxLko40y2ebgSDhenenCatLVeW/uBtnHPyhHOvG0C7TvfgecV+wHzIm5KUICgzmVpWS+IMEAeg==", "dev": true, - "requires": { - "bn.js": "^4.1.0", - "randombytes": "^2.0.1" - } + "license": "MIT" }, - "browserify-sign": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.0.4.tgz", - "integrity": "sha1-qk62jl17ZYuqa/alfmMMvXqT0pg=", + "node_modules/@sindresorhus/merge-streams": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/merge-streams/-/merge-streams-4.0.0.tgz", + "integrity": "sha512-tlqY9xq5ukxTUZBmoOp+m61cqwQD5pHJtFY3Mn8CA8ps6yghLH/Hw8UPdqg4OLmFW3IFlcXnQNmo/dh8HzXYIQ==", "dev": true, - "requires": { - "bn.js": "^4.1.1", - "browserify-rsa": "^4.0.0", - "create-hash": "^1.1.0", - "create-hmac": "^1.1.2", - "elliptic": "^6.0.0", - "inherits": "^2.0.1", - "parse-asn1": "^5.0.0" + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "browserify-zlib": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/browserify-zlib/-/browserify-zlib-0.2.0.tgz", - "integrity": "sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA==", + "node_modules/@sinonjs/commons": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.1.tgz", + "integrity": "sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==", "dev": true, - "requires": { - "pako": "~1.0.5" + "license": "BSD-3-Clause", + "dependencies": { + "type-detect": "4.0.8" } }, - "buffer": { - "version": "4.9.1", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.1.tgz", - "integrity": "sha1-bRu2AbB6TvztlwlBMgkwJ8lbwpg=", + "node_modules/@sinonjs/fake-timers": { + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.3.0.tgz", + "integrity": "sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==", "dev": true, - "requires": { - "base64-js": "^1.0.2", - "ieee754": "^1.1.4", - "isarray": "^1.0.0" - }, + "license": "BSD-3-Clause", "dependencies": { - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", - "dev": true - } + "@sinonjs/commons": "^3.0.0" } }, - "buffer-from": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", - "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==", - "dev": true - }, - "buffer-xor": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz", - "integrity": "sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk=", - "dev": true - }, - "builtin-modules": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz", - "integrity": "sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8=", - "dev": true - }, - "builtin-status-codes": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz", - "integrity": "sha1-hZgoeOIbmOHGZCXgPQF0eI9Wnug=", - "dev": true - }, - "cacache": { - "version": "12.0.3", - "resolved": "https://registry.npmjs.org/cacache/-/cacache-12.0.3.tgz", - "integrity": "sha512-kqdmfXEGFepesTuROHMs3MpFLWrPkSSpRqOw80RCflZXy/khxaArvFrQ7uJxSUduzAufc6G0g1VUCOZXxWavPw==", - "dev": true, - "requires": { - "bluebird": "^3.5.5", - "chownr": "^1.1.1", - "figgy-pudding": "^3.5.1", - "glob": "^7.1.4", - "graceful-fs": "^4.1.15", - "infer-owner": "^1.0.3", - "lru-cache": "^5.1.1", - "mississippi": "^3.0.0", - "mkdirp": "^0.5.1", - "move-concurrently": "^1.0.1", - "promise-inflight": "^1.0.1", - "rimraf": "^2.6.3", - "ssri": "^6.0.1", - "unique-filename": "^1.1.1", - "y18n": "^4.0.0" - }, - "dependencies": { - "glob": { - "version": "7.1.4", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.4.tgz", - "integrity": "sha512-hkLPepehmnKk41pUGm3sYxoFs/umurYfYJCerbXEyFIWcAzvpipAgVkBqqT9RBKMGjnq6kMuyYwha6csxbiM1A==", - "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "lru-cache": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", - "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", - "dev": true, - "requires": { - "yallist": "^3.0.2" - } - }, - "yallist": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.0.tgz", - "integrity": "sha512-6gpP93MR+VOOehKbCPchro3wFZNSNmek8A2kbkOAZLIZAYx1KP/zAqwO0sOHi3xJEb+UBz8NaYt/17UNit1Q9w==", - "dev": true - } + "node_modules/@sinonjs/samsam": { + "version": "8.0.3", + "resolved": "https://registry.npmjs.org/@sinonjs/samsam/-/samsam-8.0.3.tgz", + "integrity": "sha512-hw6HbX+GyVZzmaYNh82Ecj1vdGZrqVIn/keDTg63IgAwiQPO+xCz99uG6Woqgb4tM0mUiFENKZ4cqd7IX94AXQ==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@sinonjs/commons": "^3.0.1", + "type-detect": "^4.1.0" } }, - "cache-base": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz", - "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==", - "dev": true, - "requires": { - "collection-visit": "^1.0.0", - "component-emitter": "^1.2.1", - "get-value": "^2.0.6", - "has-value": "^1.0.0", - "isobject": "^3.0.1", - "set-value": "^2.0.0", - "to-object-path": "^0.3.0", - "union-value": "^1.0.0", - "unset-value": "^1.0.0" - } - }, - "camelcase": { + "node_modules/@sinonjs/samsam/node_modules/type-detect": { "version": "4.1.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz", - "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=", - "dev": true - }, - "capture-stack-trace": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/capture-stack-trace/-/capture-stack-trace-1.0.0.tgz", - "integrity": "sha1-Sm+gc5nCa7pH8LJJa00PtAjFVQ0=", - "dev": true + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.1.0.tgz", + "integrity": "sha512-Acylog8/luQ8L7il+geoSxhEkazvkslg7PSNKOX59mbB9cOveP5aq9h74Y7YU8yDpJwetzQQrfIwtf4Wp4LKcw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } }, - "caseless": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", - "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=", - "dev": true + "node_modules/@sinonjs/text-encoding": { + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/@sinonjs/text-encoding/-/text-encoding-0.7.3.tgz", + "integrity": "sha512-DE427ROAphMQzU4ENbliGYrBSYPXF+TtLg9S8vzeA+OF4ZKzoDdzfL8sxuMUGS/lgRhM6j1URSk9ghf7Xo1tyA==", + "deprecated": "Deprecated: no longer maintained and no longer used by Sinon packages. See\n https://github.com/sinonjs/nise/issues/243 for replacement details.", + "dev": true, + "license": "(Unlicense OR Apache-2.0)" }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "node_modules/@types/body-parser": { + "version": "1.19.6", + "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.6.tgz", + "integrity": "sha512-HLFeCYgz89uk22N5Qg3dvGvsv46B8GLvKKo1zKG4NybA8U2DiEO3w9lqGg29t/tfLRJpJ6iQxnVw4OnB7MoM9g==", "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, + "license": "MIT", "dependencies": { - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } + "@types/connect": "*", + "@types/node": "*" } }, - "chokidar": { - "version": "2.1.8", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.8.tgz", - "integrity": "sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg==", + "node_modules/@types/cheerio": { + "version": "0.22.35", + "resolved": "https://registry.npmjs.org/@types/cheerio/-/cheerio-0.22.35.tgz", + "integrity": "sha512-yD57BchKRvTV+JD53UZ6PD8KWY5g5rvvMLRnZR3EQBCZXiDT/HR+pKpMzFGlWNhFrXlo7VPZXtKvIEwZkAWOIA==", "dev": true, - "requires": { - "anymatch": "^2.0.0", - "async-each": "^1.0.1", - "braces": "^2.3.2", - "fsevents": "^1.2.7", - "glob-parent": "^3.1.0", - "inherits": "^2.0.3", - "is-binary-path": "^1.0.0", - "is-glob": "^4.0.0", - "normalize-path": "^3.0.0", - "path-is-absolute": "^1.0.0", - "readdirp": "^2.2.1", - "upath": "^1.1.1" - }, - "dependencies": { - "braces": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", - "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", - "dev": true, - "requires": { - "arr-flatten": "^1.1.0", - "array-unique": "^0.3.2", - "extend-shallow": "^2.0.1", - "fill-range": "^4.0.0", - "isobject": "^3.0.1", - "repeat-element": "^1.1.2", - "snapdragon": "^0.8.1", - "snapdragon-node": "^2.0.1", - "split-string": "^3.0.2", - "to-regex": "^3.0.1" - } - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - }, - "fill-range": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", - "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", - "dev": true, - "requires": { - "extend-shallow": "^2.0.1", - "is-number": "^3.0.0", - "repeat-string": "^1.6.1", - "to-regex-range": "^2.1.0" - } - }, - "is-number": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", - "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - } - }, - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - }, - "to-regex-range": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", - "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", - "dev": true, - "requires": { - "is-number": "^3.0.0", - "repeat-string": "^1.6.1" - } - } + "license": "MIT", + "dependencies": { + "@types/node": "*" } }, - "chownr": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.3.tgz", - "integrity": "sha512-i70fVHhmV3DtTl6nqvZOnIjbY0Pe4kAUjwHj8z0zAdgBtYrJyYwLKCCuRBQ5ppkyL0AkN7HKRnETdmdp1zqNXw==", - "dev": true - }, - "chrome-trace-event": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.2.tgz", - "integrity": "sha512-9e/zx1jw7B4CO+c/RXoCsfg/x1AfUBioy4owYH0bJprEYAx5hRFLRhWBqHAG57D0ZM4H7vxbP7bPe0VwhQRYDQ==", + "node_modules/@types/connect": { + "version": "3.4.38", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz", + "integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==", "dev": true, - "requires": { - "tslib": "^1.9.0" + "license": "MIT", + "dependencies": { + "@types/node": "*" } }, - "ci-info": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-1.1.3.tgz", - "integrity": "sha512-SK/846h/Rcy8q9Z9CAwGBLfCJ6EkjJWdpelWDufQpqVDYq2Wnnv8zlSO6AMQap02jvhVruKKpEtQOufo3pFhLg==", - "dev": true + "node_modules/@types/ejs": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/@types/ejs/-/ejs-3.1.5.tgz", + "integrity": "sha512-nv+GSx77ZtXiJzwKdsASqi+YQ5Z7vwHsTP0JY2SiQgjGckkBRKZnk8nIM+7oUZ1VCtuTz0+By4qVR7fqzp/Dfg==", + "dev": true, + "license": "MIT" }, - "cipher-base": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz", - "integrity": "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==", - "dev": true, - "requires": { - "inherits": "^2.0.1", - "safe-buffer": "^5.0.1" - } - }, - "class-utils": { - "version": "0.3.6", - "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz", - "integrity": "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==", - "dev": true, - "requires": { - "arr-union": "^3.1.0", - "define-property": "^0.2.5", - "isobject": "^3.0.0", - "static-extend": "^0.1.1" - }, - "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, - "requires": { - "is-descriptor": "^0.1.0" - } - } + "node_modules/@types/express": { + "version": "4.17.25", + "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.25.tgz", + "integrity": "sha512-dVd04UKsfpINUnK0yBoYHDF3xu7xVH4BuDotC/xGuycx4CgbP48X/KF/586bcObxT0HENHXEU8Nqtu6NR+eKhw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/body-parser": "*", + "@types/express-serve-static-core": "^4.17.33", + "@types/qs": "*", + "@types/serve-static": "^1" } }, - "cli-boxes": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-1.0.0.tgz", - "integrity": "sha1-T6kXw+WclKAEzWH47lCdplFocUM=", - "dev": true - }, - "cliui": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-4.1.0.tgz", - "integrity": "sha512-4FG+RSG9DL7uEwRUZXZn3SS34DiDPfzP0VOiEwtUWlE+AR2EIg+hSyvrIgUUfhdgR/UkAeW2QHgeP+hWrXs7jQ==", + "node_modules/@types/express-serve-static-core": { + "version": "4.19.8", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.19.8.tgz", + "integrity": "sha512-02S5fmqeoKzVZCHPZid4b8JH2eM5HzQLZWN2FohQEy/0eXTq8VXZfSN6Pcr3F6N9R/vNrj7cpgbhjie6m/1tCA==", "dev": true, - "requires": { - "string-width": "^2.1.1", - "strip-ansi": "^4.0.0", - "wrap-ansi": "^2.0.0" + "license": "MIT", + "dependencies": { + "@types/node": "*", + "@types/qs": "*", + "@types/range-parser": "*", + "@types/send": "*" } }, - "code-point-at": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", - "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", - "dev": true - }, - "coffee-script": { - "version": "1.12.7", - "resolved": "https://registry.npmjs.org/coffee-script/-/coffee-script-1.12.7.tgz", - "integrity": "sha512-fLeEhqwymYat/MpTPUjSKHVYYl0ec2mOyALEMLmzr5i1isuG+6jfI2j2d5oBO3VIzgUXgBVIcOT9uH1TFxBckw==", - "dev": true - }, - "collection-visit": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz", - "integrity": "sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=", + "node_modules/@types/fs-extra": { + "version": "9.0.13", + "resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-9.0.13.tgz", + "integrity": "sha512-nEnwB++1u5lVDM2UI4c1+5R+FYaKfaAzS4OococimjVm3nQw3TuzH5UNsocrcTBbhnerblyHj4A49qXbIiZdpA==", "dev": true, - "requires": { - "map-visit": "^1.0.0", - "object-visit": "^1.0.0" + "license": "MIT", + "dependencies": { + "@types/node": "*" } }, - "color-convert": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.1.tgz", - "integrity": "sha512-mjGanIiwQJskCC18rPR6OmrZ6fm2Lc7PeGFYwCmy5J34wC6F1PzdGL6xeMfmgicfYcNLGuVFA3WzXtIDCQSZxQ==", + "node_modules/@types/glob": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/@types/glob/-/glob-8.1.0.tgz", + "integrity": "sha512-IO+MJPVhoqz+28h1qLAcBEH2+xHMK6MTyHJc7MTnnYb6wsoLR29POVGJ7LycmVXIqyy/4/2ShP5sUwTXuOwb/w==", "dev": true, - "requires": { - "color-name": "^1.1.1" + "license": "MIT", + "dependencies": { + "@types/minimatch": "^5.1.2", + "@types/node": "*" } }, - "color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", - "dev": true - }, - "combined-stream": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.7.tgz", - "integrity": "sha512-brWl9y6vOB1xYPZcpZde3N9zDByXTosAeMDo4p1wzo6UMOX4vumB+TP1RZ76sfE6Md68Q0NJSrE/gbezd4Ul+w==", + "node_modules/@types/highlight.js": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/@types/highlight.js/-/highlight.js-10.1.0.tgz", + "integrity": "sha512-77hF2dGBsOgnvZll1vymYiNUtqJ8cJfXPD6GG/2M0aLRc29PkvB7Au6sIDjIEFcSICBhCh2+Pyq6WSRS7LUm6A==", + "deprecated": "This is a stub types definition. highlight.js provides its own type definitions, so you do not need this installed.", "dev": true, - "requires": { - "delayed-stream": "~1.0.0" + "license": "MIT", + "dependencies": { + "highlight.js": "*" } }, - "commander": { - "version": "2.11.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.11.0.tgz", - "integrity": "sha512-b0553uYA5YAEGgyYIGYROzKQ7X5RAqedkfjiZxwi0kL1g3bOaBNNZfYkzt/CL0umgD5wc9Jec2FbB98CjkMRvQ==", - "dev": true + "node_modules/@types/http-errors": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.5.tgz", + "integrity": "sha512-r8Tayk8HJnX0FztbZN7oVqGccWgw98T/0neJphO91KkmOzug1KkofZURD4UaD5uH8AqcFLfdPErnBod0u71/qg==", + "dev": true, + "license": "MIT" }, - "commondir": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", - "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=", - "dev": true + "node_modules/@types/js-yaml": { + "version": "4.0.9", + "resolved": "https://registry.npmjs.org/@types/js-yaml/-/js-yaml-4.0.9.tgz", + "integrity": "sha512-k4MGaQl5TGo/iipqb2UDG2UwjXziSWkh0uysQelTlJpX1qGlpUZYm8PnO4DxG1qBomtJUdYJ6qR6xdIah10JLg==", + "dev": true, + "license": "MIT" }, - "component-emitter": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", - "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==", - "dev": true + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true, + "license": "MIT" }, - "concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", - "dev": true - }, - "concat-stream": { - "version": "1.6.2", - "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", - "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", - "dev": true, - "requires": { - "buffer-from": "^1.0.0", - "inherits": "^2.0.3", - "readable-stream": "^2.2.2", - "typedarray": "^0.0.6" - }, - "dependencies": { - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", - "dev": true - }, - "readable-stream": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", - "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", - "dev": true, - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dev": true, - "requires": { - "safe-buffer": "~5.1.0" - } - } - } + "node_modules/@types/mime": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz", + "integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==", + "dev": true, + "license": "MIT" }, - "configstore": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/configstore/-/configstore-3.1.2.tgz", - "integrity": "sha512-vtv5HtGjcYUgFrXc6Kx747B83MRRVS5R1VTEQoXvuP+kMI+if6uywV0nDGoiydJRy4yk7h9od5Og0kxx4zUXmw==", + "node_modules/@types/minimatch": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-5.1.2.tgz", + "integrity": "sha512-K0VQKziLUWkVKiRVrx4a40iPaxTUefQmjtkQofBkYRcoaaL/8rhwDWww9qWbrgicNOgnpIsMxyNIUM4+n6dUIA==", "dev": true, - "requires": { - "dot-prop": "^4.1.0", - "graceful-fs": "^4.1.2", - "make-dir": "^1.0.0", - "unique-string": "^1.0.0", - "write-file-atomic": "^2.0.0", - "xdg-basedir": "^3.0.0" - }, - "dependencies": { - "graceful-fs": { - "version": "4.1.11", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", - "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=", - "dev": true - } - } + "license": "MIT" }, - "console-browserify": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-1.1.0.tgz", - "integrity": "sha1-8CQcRXMKn8YyOyBtvzjtx0HQuxA=", + "node_modules/@types/mocha": { + "version": "8.2.3", + "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-8.2.3.tgz", + "integrity": "sha512-ekGvFhFgrc2zYQoX4JeZPmVzZxw6Dtllga7iGHzfbYIYkAMUx/sAFP2GdFpLff+vdHXu5fl7WX9AT+TtqYcsyw==", "dev": true, - "requires": { - "date-now": "^0.1.4" - } + "license": "MIT" }, - "constants-browserify": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/constants-browserify/-/constants-browserify-1.0.0.tgz", - "integrity": "sha1-wguW2MYXdIqvHBYCF2DNJ/y4y3U=", - "dev": true + "node_modules/@types/node": { + "version": "18.19.130", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.130.tgz", + "integrity": "sha512-GRaXQx6jGfL8sKfaIDD6OupbIHBr9jv7Jnaml9tB7l4v068PAOXqfcujMMo5PhbIs6ggR1XODELqahT2R8v0fg==", + "license": "MIT", + "dependencies": { + "undici-types": "~5.26.4" + } }, - "copy-concurrently": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/copy-concurrently/-/copy-concurrently-1.0.5.tgz", - "integrity": "sha512-f2domd9fsVDFtaFcbaRZuYXwtdmnzqbADSwhSWYxYB/Q8zsdUUFMXVRwXGDMWmbEzAn1kdRrtI1T/KTFOL4X2A==", + "node_modules/@types/node-fetch": { + "version": "2.6.13", + "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.6.13.tgz", + "integrity": "sha512-QGpRVpzSaUs30JBSGPjOg4Uveu384erbHBoT1zeONvyCfwQxIkUshLAOqN/k9EjGviPRmWTTe6aH2qySWKTVSw==", "dev": true, - "requires": { - "aproba": "^1.1.1", - "fs-write-stream-atomic": "^1.0.8", - "iferr": "^0.1.5", - "mkdirp": "^0.5.1", - "rimraf": "^2.5.4", - "run-queue": "^1.0.0" + "license": "MIT", + "dependencies": { + "@types/node": "*", + "form-data": "^4.0.4" } }, - "copy-descriptor": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz", - "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=", - "dev": true - }, - "core-util-is": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", - "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", - "dev": true + "node_modules/@types/qs": { + "version": "6.15.0", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.15.0.tgz", + "integrity": "sha512-JawvT8iBVWpzTrz3EGw9BTQFg3BQNmwERdKE22vlTxawwtbyUSlMppvZYKLZzB5zgACXdXxbD3m1bXaMqP/9ow==", + "dev": true, + "license": "MIT" }, - "create-ecdh": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.3.tgz", - "integrity": "sha512-GbEHQPMOswGpKXM9kCWVrremUcBmjteUaQ01T9rkKCPDXfUHX0IoP9LpHYo2NPFampa4e+/pFDc3jQdxrxQLaw==", + "node_modules/@types/range-parser": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz", + "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==", "dev": true, - "requires": { - "bn.js": "^4.1.0", - "elliptic": "^6.0.0" - } + "license": "MIT" }, - "create-error-class": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/create-error-class/-/create-error-class-3.0.2.tgz", - "integrity": "sha1-Br56vvlHo/FKMP1hBnHUAbyot7Y=", + "node_modules/@types/semver": { + "version": "7.7.1", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.7.1.tgz", + "integrity": "sha512-FmgJfu+MOcQ370SD0ev7EI8TlCAfKYU+B4m5T3yXc1CiRN94g/SZPtsCkk506aUDtlMnFZvasDwHHUcZUEaYuA==", "dev": true, - "requires": { - "capture-stack-trace": "^1.0.0" - } + "license": "MIT" }, - "create-hash": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", - "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==", + "node_modules/@types/send": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@types/send/-/send-1.2.1.tgz", + "integrity": "sha512-arsCikDvlU99zl1g69TcAB3mzZPpxgw0UQnaHeC1Nwb015xp8bknZv5rIfri9xTOcMuaVgvabfIRA7PSZVuZIQ==", "dev": true, - "requires": { - "cipher-base": "^1.0.1", - "inherits": "^2.0.1", - "md5.js": "^1.3.4", - "ripemd160": "^2.0.1", - "sha.js": "^2.4.0" + "license": "MIT", + "dependencies": { + "@types/node": "*" } }, - "create-hmac": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz", - "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==", + "node_modules/@types/serve-static": { + "version": "1.15.10", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.10.tgz", + "integrity": "sha512-tRs1dB+g8Itk72rlSI2ZrW6vZg0YrLI81iQSTkMmOqnqCaNr/8Ek4VwWcN5vZgCYWbg/JJSGBlUaYGAOP73qBw==", "dev": true, - "requires": { - "cipher-base": "^1.0.3", - "create-hash": "^1.1.0", - "inherits": "^2.0.1", - "ripemd160": "^2.0.0", - "safe-buffer": "^5.0.1", - "sha.js": "^2.4.8" + "license": "MIT", + "dependencies": { + "@types/http-errors": "*", + "@types/node": "*", + "@types/send": "<1" } }, - "cross-spawn": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz", - "integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=", - "dev": true, - "requires": { - "lru-cache": "^4.0.1", - "shebang-command": "^1.2.0", - "which": "^1.2.9" - } - }, - "crypto-browserify": { - "version": "3.12.0", - "resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.12.0.tgz", - "integrity": "sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg==", - "dev": true, - "requires": { - "browserify-cipher": "^1.0.0", - "browserify-sign": "^4.0.0", - "create-ecdh": "^4.0.0", - "create-hash": "^1.1.0", - "create-hmac": "^1.1.0", - "diffie-hellman": "^5.0.0", - "inherits": "^2.0.1", - "pbkdf2": "^3.0.3", - "public-encrypt": "^4.0.0", - "randombytes": "^2.0.0", - "randomfill": "^1.0.3" - } - }, - "crypto-random-string": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-1.0.0.tgz", - "integrity": "sha1-ojD2T1aDEOFJgAmUB5DsmVRbyn4=", - "dev": true - }, - "cson-parser": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/cson-parser/-/cson-parser-2.0.0.tgz", - "integrity": "sha1-Y2ch4yK5fAKOj9GK/1iYwAIMKk0=", + "node_modules/@types/serve-static/node_modules/@types/send": { + "version": "0.17.6", + "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.6.tgz", + "integrity": "sha512-Uqt8rPBE8SY0RK8JB1EzVOIZ32uqy8HwdxCnoCOsYrvnswqmFZ/k+9Ikidlk/ImhsdvBsloHbAlewb2IEBV/Og==", "dev": true, - "requires": { - "coffee-script": "^1.10.0" + "license": "MIT", + "dependencies": { + "@types/mime": "^1", + "@types/node": "*" } }, - "cyclist": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/cyclist/-/cyclist-1.0.1.tgz", - "integrity": "sha1-WW6WmP0MgOEgOMK4LW6xs1tiJNk=", - "dev": true - }, - "dashdash": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", - "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", + "node_modules/@types/sinon": { + "version": "10.0.20", + "resolved": "https://registry.npmjs.org/@types/sinon/-/sinon-10.0.20.tgz", + "integrity": "sha512-2APKKruFNCAZgx3daAyACGzWuJ028VVCUDk6o2rw/Z4PXT0ogwdV4KUegW0MwVs0Zu59auPXbbuBJHF12Sx1Eg==", "dev": true, - "requires": { - "assert-plus": "^1.0.0" + "license": "MIT", + "dependencies": { + "@types/sinonjs__fake-timers": "*" } }, - "date-now": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/date-now/-/date-now-0.1.4.tgz", - "integrity": "sha1-6vQ5/U1ISK105cx9vvIAZyueNFs=", - "dev": true - }, - "debug": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", - "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "node_modules/@types/sinonjs__fake-timers": { + "version": "15.0.1", + "resolved": "https://registry.npmjs.org/@types/sinonjs__fake-timers/-/sinonjs__fake-timers-15.0.1.tgz", + "integrity": "sha512-Ko2tjWJq8oozHzHV+reuvS5KYIRAokHnGbDwGh/J64LntgpbuylF74ipEL24HCyRjf9FOlBiBHWBR1RlVKsI1w==", "dev": true, - "requires": { - "ms": "^2.1.1" - } + "license": "MIT" }, - "decamelize": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", - "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", - "dev": true + "node_modules/@types/vscode": { + "version": "1.110.0", + "resolved": "https://registry.npmjs.org/@types/vscode/-/vscode-1.110.0.tgz", + "integrity": "sha512-AGuxUEpU4F4mfuQjxPPaQVyuOMhs+VT/xRok1jiHVBubHK7lBRvCuOMZG0LKUwxncrPorJ5qq/uil3IdZBd5lA==", + "dev": true, + "license": "MIT" }, - "decode-uri-component": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", - "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=", - "dev": true + "node_modules/@types/winreg": { + "version": "1.2.36", + "resolved": "https://registry.npmjs.org/@types/winreg/-/winreg-1.2.36.tgz", + "integrity": "sha512-DtafHy5A8hbaosXrbr7YdjQZaqVewXmiasRS5J4tYMzt3s1gkh40ixpxgVFfKiQ0JIYetTJABat47v9cpr/sQg==", + "dev": true, + "license": "MIT" }, - "deep-extend": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.5.1.tgz", - "integrity": "sha512-N8vBdOa+DF7zkRrDCsaOXoCs/E2fJfx9B9MrKnnSiHNh4ws7eSys6YQE4KvT1cecKmOASYQBhbKjeuDD9lT81w==", - "dev": true + "node_modules/@types/ws": { + "version": "8.18.1", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.18.1.tgz", + "integrity": "sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==", + "license": "MIT", + "dependencies": { + "@types/node": "*" + } }, - "define-properties": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", - "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.62.0.tgz", + "integrity": "sha512-TiZzBSJja/LbhNPvk6yc0JrX9XqhQ0hdh6M2svYfsHGejaKFIAGd9MQ+ERIMzLGlN/kZoYIgdxFV0PuljTKXag==", "dev": true, - "requires": { - "object-keys": "^1.0.12" + "license": "MIT", + "dependencies": { + "@eslint-community/regexpp": "^4.4.0", + "@typescript-eslint/scope-manager": "5.62.0", + "@typescript-eslint/type-utils": "5.62.0", + "@typescript-eslint/utils": "5.62.0", + "debug": "^4.3.4", + "graphemer": "^1.4.0", + "ignore": "^5.2.0", + "natural-compare-lite": "^1.4.0", + "semver": "^7.3.7", + "tsutils": "^3.21.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^5.0.0", + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } } }, - "define-property": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz", - "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==", - "dev": true, - "requires": { - "is-descriptor": "^1.0.2", - "isobject": "^3.0.1" - }, - "dependencies": { - "is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - } + "node_modules/@typescript-eslint/parser": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.62.0.tgz", + "integrity": "sha512-VlJEV0fOQ7BExOsHYAGrgbEiZoi8D+Bl2+f6V2RrXerRSylnp+ZBHmPvaIa8cz0Ajx7WO7Z5RqfgYg7ED1nRhA==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "@typescript-eslint/scope-manager": "5.62.0", + "@typescript-eslint/types": "5.62.0", + "@typescript-eslint/typescript-estree": "5.62.0", + "debug": "^4.3.4" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true } } }, - "delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", - "dev": true - }, - "des.js": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/des.js/-/des.js-1.0.0.tgz", - "integrity": "sha1-wHTS4qpqipoH29YfmhXCzYPsjsw=", + "node_modules/@typescript-eslint/scope-manager": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.62.0.tgz", + "integrity": "sha512-VXuvVvZeQCQb5Zgf4HAxc04q5j+WrNAtNh9OwCsCgpKqESMTu3tF/jhZ3xG6T4NZwWl65Bg8KuS2uEvhSfLl0w==", "dev": true, - "requires": { - "inherits": "^2.0.1", - "minimalistic-assert": "^1.0.0" + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "5.62.0", + "@typescript-eslint/visitor-keys": "5.62.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" } }, - "detect-file": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/detect-file/-/detect-file-1.0.0.tgz", - "integrity": "sha1-8NZtA2cqglyxtzvbP+YjEMjlUrc=", - "dev": true - }, - "diff": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz", - "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==", - "dev": true - }, - "diffie-hellman": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz", - "integrity": "sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==", + "node_modules/@typescript-eslint/type-utils": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.62.0.tgz", + "integrity": "sha512-xsSQreu+VnfbqQpW5vnCJdq1Z3Q0U31qiWmRhr98ONQmcp/yhiPJFPq8MXiJVLiksmOKSjIldZzkebzHuCGzew==", "dev": true, - "requires": { - "bn.js": "^4.1.0", - "miller-rabin": "^4.0.0", - "randombytes": "^2.0.0" + "license": "MIT", + "dependencies": { + "@typescript-eslint/typescript-estree": "5.62.0", + "@typescript-eslint/utils": "5.62.0", + "debug": "^4.3.4", + "tsutils": "^3.21.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "*" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } } }, - "domain-browser": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/domain-browser/-/domain-browser-1.2.0.tgz", - "integrity": "sha512-jnjyiM6eRyZl2H+W8Q/zLMA481hzi0eszAaBUzIVnmYVDBbnLxVNnfu1HgEBvCbL+71FrxMl3E6lpKH7Ge3OXA==", - "dev": true - }, - "dot-prop": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-4.2.0.tgz", - "integrity": "sha512-tUMXrxlExSW6U2EXiiKGSBVdYgtV8qlHL+C10TsW4PURY/ic+eaysnSkwB4kA/mBlCyy/IKDJ+Lc3wbWeaXtuQ==", + "node_modules/@typescript-eslint/types": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.62.0.tgz", + "integrity": "sha512-87NVngcbVXUahrRTqIK27gD2t5Cu1yuCXxbLcFtCzZGlfyVWWh8mLHkoxzjsB6DDNnvdL+fW8MiwPEJyGJQDgQ==", "dev": true, - "requires": { - "is-obj": "^1.0.0" + "license": "MIT", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" } }, - "duplexer3": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.4.tgz", - "integrity": "sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI=", - "dev": true - }, - "duplexify": { - "version": "3.7.1", - "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.7.1.tgz", - "integrity": "sha512-07z8uv2wMyS51kKhD1KsdXJg5WQ6t93RneqRxUHnskXVtlYYkLqM0gqStQZ3pj073g687jPCHrqNfCzawLYh5g==", + "node_modules/@typescript-eslint/typescript-estree": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.62.0.tgz", + "integrity": "sha512-CmcQ6uY7b9y694lKdRB8FEel7JbU/40iSAPomu++SjLMntB+2Leay2LO6i8VnJk58MtE9/nQSFIH6jpyRWyYzA==", "dev": true, - "requires": { - "end-of-stream": "^1.0.0", - "inherits": "^2.0.1", - "readable-stream": "^2.0.0", - "stream-shift": "^1.0.0" - }, + "license": "BSD-2-Clause", "dependencies": { - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", - "dev": true - }, - "readable-stream": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", - "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", - "dev": true, - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dev": true, - "requires": { - "safe-buffer": "~5.1.0" - } + "@typescript-eslint/types": "5.62.0", + "@typescript-eslint/visitor-keys": "5.62.0", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "semver": "^7.3.7", + "tsutils": "^3.21.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true } } }, - "ecc-jsbn": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", - "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=", + "node_modules/@typescript-eslint/utils": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.62.0.tgz", + "integrity": "sha512-n8oxjeb5aIbPFEtmQxQYOLI0i9n5ySBEY/ZEHHZqKQSFnxio1rv6dthascc9dLuwrL0RC5mPCxB7vnAVGAYWAQ==", "dev": true, - "requires": { - "jsbn": "~0.1.0", - "safer-buffer": "^2.1.0" + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", + "@types/json-schema": "^7.0.9", + "@types/semver": "^7.3.12", + "@typescript-eslint/scope-manager": "5.62.0", + "@typescript-eslint/types": "5.62.0", + "@typescript-eslint/typescript-estree": "5.62.0", + "eslint-scope": "^5.1.1", + "semver": "^7.3.7" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, - "elliptic": { - "version": "6.5.1", - "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.1.tgz", - "integrity": "sha512-xvJINNLbTeWQjrl6X+7eQCrIy/YPv5XCpKW6kB5mKvtnGILoLDcySuwomfdzt0BMdLNVnuRNTuzKNHj0bva1Cg==", + "node_modules/@typescript-eslint/visitor-keys": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.62.0.tgz", + "integrity": "sha512-07ny+LHRzQXepkGg6w0mFY41fVUNBrL2Roj/++7V1txKugfjm/Ci/qSND03r2RhlJhJYMcTn9AhhSSqQp0Ysyw==", "dev": true, - "requires": { - "bn.js": "^4.4.0", - "brorand": "^1.0.1", - "hash.js": "^1.0.0", - "hmac-drbg": "^1.0.0", - "inherits": "^2.0.1", - "minimalistic-assert": "^1.0.0", - "minimalistic-crypto-utils": "^1.0.0" + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "5.62.0", + "eslint-visitor-keys": "^3.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" } }, - "emoji-regex": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", - "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", - "dev": true + "node_modules/@vscode/test-electron": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/@vscode/test-electron/-/test-electron-2.5.2.tgz", + "integrity": "sha512-8ukpxv4wYe0iWMRQU18jhzJOHkeGKbnw7xWRX3Zw1WJA4cEKbHcmmLPdPrPtL6rhDcrlCZN+xKRpv09n4gRHYg==", + "dev": true, + "license": "MIT", + "dependencies": { + "http-proxy-agent": "^7.0.2", + "https-proxy-agent": "^7.0.5", + "jszip": "^3.10.1", + "ora": "^8.1.0", + "semver": "^7.6.2" + }, + "engines": { + "node": ">=16" + } }, - "emojis-list": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-2.1.0.tgz", - "integrity": "sha1-TapNnbAPmBmIDHn6RXrlsJof04k=", - "dev": true + "node_modules/acorn": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", + "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } }, - "end-of-stream": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.1.tgz", - "integrity": "sha512-1MkrZNvWTKCaigbn+W15elq2BB/L22nqrSY5DKlo3X6+vclJm8Bb5djXJBmEX6fS3+zCh/F4VBK5Z2KxJt4s2Q==", + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", "dev": true, - "requires": { - "once": "^1.4.0" + "license": "MIT", + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, - "enhanced-resolve": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-4.1.0.tgz", - "integrity": "sha512-F/7vkyTtyc/llOIn8oWclcB25KdRaiPBpZYDgJHgh/UHtpgT2p2eldQgtQnLtUvfMKPKxbRaQM/hHkvLHt1Vng==", + "node_modules/ag-grid-community": { + "version": "31.3.4", + "resolved": "https://registry.npmjs.org/ag-grid-community/-/ag-grid-community-31.3.4.tgz", + "integrity": "sha512-jOxQO86C6eLnk1GdP24HB6aqaouFzMWizgfUwNY5MnetiWzz9ZaAmOGSnW/XBvdjXvC5Fpk3gSbvVKKQ7h9kBw==", + "license": "MIT" + }, + "node_modules/agent-base": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", + "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==", "dev": true, - "requires": { - "graceful-fs": "^4.1.2", - "memory-fs": "^0.4.0", - "tapable": "^1.0.0" + "license": "MIT", + "engines": { + "node": ">= 14" } }, - "errno": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.7.tgz", - "integrity": "sha512-MfrRBDWzIWifgq6tJj60gkAwtLNb6sQPlcFrSOflcP1aFmmruKQ2wRnze/8V6kgyz7H3FF8Npzv78mZ7XLLflg==", + "node_modules/ajv": { + "version": "6.14.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.14.0.tgz", + "integrity": "sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw==", "dev": true, - "requires": { - "prr": "~1.0.1" + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" } }, - "error-ex": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.1.tgz", - "integrity": "sha1-+FWobOYa3E6GIcPNoh56dhLDqNw=", + "node_modules/ansi-colors": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", + "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==", "dev": true, - "requires": { - "is-arrayish": "^0.2.1" + "license": "MIT", + "engines": { + "node": ">=6" } }, - "es-abstract": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.13.0.tgz", - "integrity": "sha512-vDZfg/ykNxQVwup/8E1BZhVzFfBxs9NqMzGcvIJrqg5k2/5Za2bWo40dK2J1pgLngZ7c+Shh8lwYtLGyrwPutg==", + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "dev": true, - "requires": { - "es-to-primitive": "^1.2.0", - "function-bind": "^1.1.1", - "has": "^1.0.3", - "is-callable": "^1.1.4", - "is-regex": "^1.0.4", - "object-keys": "^1.0.12" + "license": "MIT", + "engines": { + "node": ">=8" } }, - "es-to-primitive": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.0.tgz", - "integrity": "sha512-qZryBOJjV//LaxLTV6UC//WewneB3LcXOL9NP++ozKVXsIIIpm/2c13UDiD9Jp2eThsecw9m3jPqDwTyobcdbg==", + "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==", "dev": true, - "requires": { - "is-callable": "^1.1.4", - "is-date-object": "^1.0.1", - "is-symbol": "^1.0.2" + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "es6-promise": { - "version": "4.2.6", - "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.6.tgz", - "integrity": "sha512-aRVgGdnmW2OiySVPUC9e6m+plolMAJKjZnQlCwNSuK5yQ0JN61DZSO1X1Ufd1foqWRAlig0rhduTCHe7sVtK5Q==", - "dev": true + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "license": "Python-2.0" }, - "es6-promisify": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/es6-promisify/-/es6-promisify-5.0.0.tgz", - "integrity": "sha1-UQnWLz5W6pZ8S2NQWu8IKRyKUgM=", + "node_modules/array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", "dev": true, - "requires": { - "es6-promise": "^4.0.3" + "license": "MIT", + "engines": { + "node": ">=8" } }, - "escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", - "dev": true - }, - "eslint-scope": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-4.0.3.tgz", - "integrity": "sha512-p7VutNr1O/QrxysMo3E45FjYDTeXBy0iTltPFNSqKAIfjDSXC+4dj+qfyuD8bfAXrW/y6lW3O76VaYNPKfpKrg==", + "node_modules/astral-regex": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", + "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", "dev": true, - "requires": { - "esrecurse": "^4.1.0", - "estraverse": "^4.1.1" + "license": "MIT", + "engines": { + "node": ">=8" } }, - "esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "dev": true + "node_modules/async": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", + "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==", + "license": "MIT" }, - "esrecurse": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.2.1.tgz", - "integrity": "sha512-64RBB++fIOAXPw3P9cy89qfMlvZEXZkqqJkjqqXIvzP5ezRZjW+lPWjw35UX/3EhUPFYbg5ER4JYgDw4007/DQ==", + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", "dev": true, - "requires": { - "estraverse": "^4.1.0" - } + "license": "MIT" }, - "estraverse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", - "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", - "dev": true + "node_modules/await-semaphore": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/await-semaphore/-/await-semaphore-0.1.3.tgz", + "integrity": "sha512-d1W2aNSYcz/sxYO4pMGX9vq65qOTu0P800epMud+6cYYX0QcT7zyqcxec3VWzpgvdXo57UWmVbZpLMjX2m1I7Q==", + "license": "MIT" }, - "esutils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", - "dev": true + "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==", + "license": "MIT" }, - "events": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/events/-/events-3.0.0.tgz", - "integrity": "sha512-Dc381HFWJzEOhQ+d8pkNon++bk9h6cdAoAj4iE6Q4y6xgTzySWXlKn05/TVNpjnfRqi/X0EpJEJohPjNI3zpVA==", - "dev": true + "node_modules/boolbase": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", + "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==", + "license": "ISC" }, - "evp_bytestokey": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz", - "integrity": "sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==", - "dev": true, - "requires": { - "md5.js": "^1.3.4", - "safe-buffer": "^5.1.1" - } - }, - "execa": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-0.7.0.tgz", - "integrity": "sha1-lEvs00zEHuMqY6n68nrVpl/Fl3c=", - "dev": true, - "requires": { - "cross-spawn": "^5.0.1", - "get-stream": "^3.0.0", - "is-stream": "^1.1.0", - "npm-run-path": "^2.0.0", - "p-finally": "^1.0.0", - "signal-exit": "^3.0.0", - "strip-eof": "^1.0.0" - } - }, - "expand-brackets": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", - "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", - "dev": true, - "requires": { - "debug": "^2.3.3", - "define-property": "^0.2.5", - "extend-shallow": "^2.0.1", - "posix-character-classes": "^0.1.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - }, - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, - "requires": { - "is-descriptor": "^0.1.0" - } - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true - } + "node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" } }, - "expand-tilde": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/expand-tilde/-/expand-tilde-2.0.2.tgz", - "integrity": "sha1-l+gBqgUt8CRU3kawK/YhZCzchQI=", + "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==", "dev": true, - "requires": { - "homedir-polyfill": "^1.0.1" + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" } }, - "extend": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", - "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", - "dev": true + "node_modules/browser-stdout": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", + "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", + "dev": true, + "license": "ISC" }, - "extend-shallow": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", - "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", - "dev": true, - "requires": { - "assign-symbols": "^1.0.0", - "is-extendable": "^1.0.1" - }, - "dependencies": { - "is-extendable": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", - "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", - "dev": true, - "requires": { - "is-plain-object": "^2.0.4" - } - } + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" } }, - "extglob": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", - "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", - "dev": true, - "requires": { - "array-unique": "^0.3.2", - "define-property": "^1.0.0", - "expand-brackets": "^2.1.4", - "extend-shallow": "^2.0.1", - "fragment-cache": "^0.2.1", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - }, - "dependencies": { - "define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", - "dev": true, - "requires": { - "is-descriptor": "^1.0.0" - } - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - }, - "is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - } - } + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" } }, - "extsprintf": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", - "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=", - "dev": true + "node_modules/camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } }, - "fast-deep-equal": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz", - "integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=", - "dev": true + "node_modules/cancellationtoken": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/cancellationtoken/-/cancellationtoken-2.2.0.tgz", + "integrity": "sha512-uF4sHE5uh2VdEZtIRJKGoXAD9jm7bFY0tDRCzH4iLp262TOJ2lrtNHjMG2zc8H+GICOpELIpM7CGW5JeWnb3Hg==", + "license": "MIT" }, - "fast-json-stable-stringify": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz", - "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=", - "dev": true - }, - "figgy-pudding": { - "version": "3.5.1", - "resolved": "https://registry.npmjs.org/figgy-pudding/-/figgy-pudding-3.5.1.tgz", - "integrity": "sha512-vNKxJHTEKNThjfrdJwHc7brvM6eVevuO5nTj6ez8ZQ1qbXTvGthucRF7S4vf2cr71QVnT70V34v0S1DyQsti0w==", - "dev": true - }, - "fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "dev": true, - "requires": { - "to-regex-range": "^5.0.1" + "node_modules/caught": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/caught/-/caught-0.1.3.tgz", + "integrity": "sha512-DTWI84qfoqHEV5jHRpsKNnEisVCeuBDscXXaXyRLXC+4RD6rFftUNuTElcQ7LeO7w622pfzWkA1f6xu5qEAidw==", + "license": "MIT" + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "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/cheerio": { + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.0.0-rc.12.tgz", + "integrity": "sha512-VqR8m68vM46BNnuZ5NtnGBKIE/DfN0cRIzg9n40EIq9NOv90ayxLBXA8fXC5gquFRGJSTRqBq25Jt2ECLR431Q==", + "license": "MIT", + "dependencies": { + "cheerio-select": "^2.1.0", + "dom-serializer": "^2.0.0", + "domhandler": "^5.0.3", + "domutils": "^3.0.1", + "htmlparser2": "^8.0.1", + "parse5": "^7.0.0", + "parse5-htmlparser2-tree-adapter": "^7.0.0" + }, + "engines": { + "node": ">= 6" + }, + "funding": { + "url": "https://github.com/cheeriojs/cheerio?sponsor=1" } }, - "find-cache-dir": { + "node_modules/cheerio-select": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-2.1.0.tgz", - "integrity": "sha512-Tq6PixE0w/VMFfCgbONnkiQIVol/JJL7nRMi20fqzA4NRs9AfeqMGeRdPi3wIhYkxjeBaWh2rxwapn5Tu3IqOQ==", - "dev": true, - "requires": { - "commondir": "^1.0.1", - "make-dir": "^2.0.0", - "pkg-dir": "^3.0.0" - }, - "dependencies": { - "make-dir": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", - "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", - "dev": true, - "requires": { - "pify": "^4.0.1", - "semver": "^5.6.0" - } - }, - "pify": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", - "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", - "dev": true - }, - "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "dev": true - } + "resolved": "https://registry.npmjs.org/cheerio-select/-/cheerio-select-2.1.0.tgz", + "integrity": "sha512-9v9kG0LvzrlcungtnJtpGNxY+fzECQKhK4EGJX2vByejiMX84MFNQw4UxPJl3bFbTMw+Dfs37XaIkCwTZfLh4g==", + "license": "BSD-2-Clause", + "dependencies": { + "boolbase": "^1.0.0", + "css-select": "^5.1.0", + "css-what": "^6.1.0", + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3", + "domutils": "^3.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" } }, - "find-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", - "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "node_modules/chokidar": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", + "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", "dev": true, - "requires": { - "locate-path": "^3.0.0" + "license": "MIT", + "dependencies": { + "readdirp": "^4.0.1" + }, + "engines": { + "node": ">= 14.16.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" } }, - "findup-sync": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-3.0.0.tgz", - "integrity": "sha512-YbffarhcicEhOrm4CtrwdKBdCuz576RLdhJDsIfvNtxUuhdRet1qZcsMjqbePtAseKdAnDyM/IyXbu7PRPRLYg==", + "node_modules/cli-cursor": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-5.0.0.tgz", + "integrity": "sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==", "dev": true, - "requires": { - "detect-file": "^1.0.0", - "is-glob": "^4.0.0", - "micromatch": "^3.0.4", - "resolve-dir": "^1.0.1" - }, - "dependencies": { - "braces": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", - "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", - "dev": true, - "requires": { - "arr-flatten": "^1.1.0", - "array-unique": "^0.3.2", - "extend-shallow": "^2.0.1", - "fill-range": "^4.0.0", - "isobject": "^3.0.1", - "repeat-element": "^1.1.2", - "snapdragon": "^0.8.1", - "snapdragon-node": "^2.0.1", - "split-string": "^3.0.2", - "to-regex": "^3.0.1" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "fill-range": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", - "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", - "dev": true, - "requires": { - "extend-shallow": "^2.0.1", - "is-number": "^3.0.0", - "repeat-string": "^1.6.1", - "to-regex-range": "^2.1.0" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "is-number": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", - "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "micromatch": { - "version": "3.1.10", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", - "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", - "dev": true, - "requires": { - "arr-diff": "^4.0.0", - "array-unique": "^0.3.2", - "braces": "^2.3.1", - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "extglob": "^2.0.4", - "fragment-cache": "^0.2.1", - "kind-of": "^6.0.2", - "nanomatch": "^1.2.9", - "object.pick": "^1.3.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.2" - } - }, - "to-regex-range": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", - "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", - "dev": true, - "requires": { - "is-number": "^3.0.0", - "repeat-string": "^1.6.1" - } - } + "license": "MIT", + "dependencies": { + "restore-cursor": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "flat": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/flat/-/flat-4.1.0.tgz", - "integrity": "sha512-Px/TiLIznH7gEDlPXcUD4KnBusa6kR6ayRUVcnEAbreRIuhkqow/mun59BuRXwoYk7ZQOLW1ZM05ilIvK38hFw==", + "node_modules/cli-spinners": { + "version": "2.9.2", + "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.2.tgz", + "integrity": "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==", "dev": true, - "requires": { - "is-buffer": "~2.0.3" + "license": "MIT", + "engines": { + "node": ">=6" }, - "dependencies": { - "is-buffer": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.3.tgz", - "integrity": "sha512-U15Q7MXTuZlrbymiz95PJpZxu8IlipAp4dtS3wOdgPXx3mqBnslrWU14kxfHB+Py/+2PVKSr37dMAgM2A4uArw==", - "dev": true - } + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "flush-write-stream": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/flush-write-stream/-/flush-write-stream-1.1.1.tgz", - "integrity": "sha512-3Z4XhFZ3992uIq0XOqb9AreonueSYphE6oYbpt5+3u06JWklbsPkNv3ZKkP9Bz/r+1MWCaMoSQ28P85+1Yc77w==", + "node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", "dev": true, - "requires": { - "inherits": "^2.0.3", - "readable-stream": "^2.3.6" - }, + "license": "ISC", "dependencies": { - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", - "dev": true - }, - "readable-stream": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", - "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", - "dev": true, - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dev": true, - "requires": { - "safe-buffer": "~5.1.0" - } - } + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" } }, - "for-in": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", - "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=", - "dev": true - }, - "forever-agent": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", - "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=", - "dev": true - }, - "form-data": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", - "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", - "dev": true, - "requires": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.6", - "mime-types": "^2.1.12" - } + "node_modules/cliui/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" }, - "fragment-cache": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz", - "integrity": "sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk=", + "node_modules/cliui/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", "dev": true, - "requires": { - "map-cache": "^0.2.2" + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" } }, - "from2": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/from2/-/from2-2.3.0.tgz", - "integrity": "sha1-i/tVAr3kpNNs/e6gB/zKIdfjgq8=", + "node_modules/cliui/node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", "dev": true, - "requires": { - "inherits": "^2.0.1", - "readable-stream": "^2.0.0" + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "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==", + "dev": true, + "license": "MIT", "dependencies": { - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", - "dev": true - }, - "readable-stream": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", - "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", - "dev": true, - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dev": true, - "requires": { - "safe-buffer": "~5.1.0" - } + "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==", + "dev": true, + "license": "MIT" + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dev": true, + "license": "MIT", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/comment-parser": { + "version": "1.1.6-beta.0", + "resolved": "https://registry.npmjs.org/comment-parser/-/comment-parser-1.1.6-beta.0.tgz", + "integrity": "sha512-q3cA8TSMyqW7wcPSYWzbO/rMahnXgzs4SLG/UIWXdEsnXTFPZkEkWAdNgPiHig2OzxgpPLOh4WwsmClDxndwHw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true, + "license": "MIT" + }, + "node_modules/core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/cross-fetch": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.2.0.tgz", + "integrity": "sha512-Q+xVJLoGOeIMXZmbUK4HYk+69cQH6LudR0Vu/pRm2YlU/hDV9CiS0gKUMaWY5f2NeUH9C1nV3bsTlCo0FsTV1Q==", + "license": "MIT", + "dependencies": { + "node-fetch": "^2.7.0" + } + }, + "node_modules/cross-spawn": { + "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", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/crypto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/crypto/-/crypto-1.0.1.tgz", + "integrity": "sha512-VxBKmeNcqQdiUQUW2Tzq0t377b54N2bMtXO/qiLa+6eRRmmC4qT3D4OnTGoT/U6O9aklQ/jTwbOtRMTTY8G0Ig==", + "deprecated": "This package is no longer supported. It's now a built-in Node module. If you've depended on crypto, you should switch to the one that's built-in.", + "license": "ISC" + }, + "node_modules/css-select": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-5.2.2.tgz", + "integrity": "sha512-TizTzUddG/xYLA3NXodFM0fSbNizXjOKhqiQQwvhlspadZokn1KDy0NZFS0wuEubIYAV5/c1/lAr0TaaFXEXzw==", + "license": "BSD-2-Clause", + "dependencies": { + "boolbase": "^1.0.0", + "css-what": "^6.1.0", + "domhandler": "^5.0.2", + "domutils": "^3.0.1", + "nth-check": "^2.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/css-what": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.2.2.tgz", + "integrity": "sha512-u/O3vwbptzhMs3L1fQE82ZSLHQQfto5gyZzwteVIEyeaY5Fc7R4dapF/BvRoSYFeqfBk4m0V1Vafq5Pjv25wvA==", + "license": "BSD-2-Clause", + "engines": { + "node": ">= 6" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true } } }, - "fs-extra": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", - "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", - "requires": { - "graceful-fs": "^4.2.0", - "jsonfile": "^4.0.0", - "universalify": "^0.1.0" + "node_modules/decamelize": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", + "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/diff": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-7.0.0.tgz", + "integrity": "sha512-PJWHUb1RFevKCwaFA9RlG5tCd+FO5iRh9A8HEtkmBH2Li03iJriB6m6JIN4rGz3K3JLawI7/veA1xzRKP6ISBw==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-type": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "esutils": "^2.0.2" }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/dom-serializer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", + "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", + "license": "MIT", "dependencies": { - "graceful-fs": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.0.tgz", - "integrity": "sha512-jpSvDPV4Cq/bgtpndIWbI5hmYxhQGHPC4d4cqBPb4DLniCfhJokdXhwhaDuLBGLQdvvRum/UiX6ECVIPvDXqdg==" + "domelementtype": "^2.3.0", + "domhandler": "^5.0.2", + "entities": "^4.2.0" + }, + "funding": { + "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" + } + }, + "node_modules/domelementtype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", + "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fb55" } + ], + "license": "BSD-2-Clause" + }, + "node_modules/domhandler": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", + "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", + "license": "BSD-2-Clause", + "dependencies": { + "domelementtype": "^2.3.0" + }, + "engines": { + "node": ">= 4" + }, + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" } }, - "fs-write-stream-atomic": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/fs-write-stream-atomic/-/fs-write-stream-atomic-1.0.10.tgz", - "integrity": "sha1-tH31NJPvkR33VzHnCp3tAYnbQMk=", + "node_modules/domutils": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.2.2.tgz", + "integrity": "sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==", + "license": "BSD-2-Clause", + "dependencies": { + "dom-serializer": "^2.0.0", + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3" + }, + "funding": { + "url": "https://github.com/fb55/domutils?sponsor=1" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "dev": true, + "license": "MIT" + }, + "node_modules/ejs": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.10.tgz", + "integrity": "sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==", + "license": "Apache-2.0", + "dependencies": { + "jake": "^10.8.5" + }, + "bin": { + "ejs": "bin/cli.js" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true, + "license": "MIT" + }, + "node_modules/enquirer": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.4.1.tgz", + "integrity": "sha512-rRqJg/6gd538VHvR3PSrdRBb/1Vy2YfzHqzvbhGIQpDRKIa4FgV/54b5Q1xYSxOOwKvjXweS26E0Q+nAMwp2pQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-colors": "^4.1.1", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", "dev": true, - "requires": { - "graceful-fs": "^4.1.2", - "iferr": "^0.1.5", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/esbuild": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.4.tgz", + "integrity": "sha512-Rq4vbHnYkK5fws5NF7MYTU68FPRE1ajX7heQ/8QXXWqNgqqJ/GkmmyxIzUnf2Sr/bakf8l54716CcMGHYhMrrQ==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.27.4", + "@esbuild/android-arm": "0.27.4", + "@esbuild/android-arm64": "0.27.4", + "@esbuild/android-x64": "0.27.4", + "@esbuild/darwin-arm64": "0.27.4", + "@esbuild/darwin-x64": "0.27.4", + "@esbuild/freebsd-arm64": "0.27.4", + "@esbuild/freebsd-x64": "0.27.4", + "@esbuild/linux-arm": "0.27.4", + "@esbuild/linux-arm64": "0.27.4", + "@esbuild/linux-ia32": "0.27.4", + "@esbuild/linux-loong64": "0.27.4", + "@esbuild/linux-mips64el": "0.27.4", + "@esbuild/linux-ppc64": "0.27.4", + "@esbuild/linux-riscv64": "0.27.4", + "@esbuild/linux-s390x": "0.27.4", + "@esbuild/linux-x64": "0.27.4", + "@esbuild/netbsd-arm64": "0.27.4", + "@esbuild/netbsd-x64": "0.27.4", + "@esbuild/openbsd-arm64": "0.27.4", + "@esbuild/openbsd-x64": "0.27.4", + "@esbuild/openharmony-arm64": "0.27.4", + "@esbuild/sunos-x64": "0.27.4", + "@esbuild/win32-arm64": "0.27.4", + "@esbuild/win32-ia32": "0.27.4", + "@esbuild/win32-x64": "0.27.4" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint": { + "version": "7.32.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.32.0.tgz", + "integrity": "sha512-VHZ8gX+EDfz+97jGcgyGCyRia/dPOd6Xh9yPv8Bl1+SoaIwD+a/vlrOmGRUyOYu7MwUhc7CxqeaDZU13S4+EpA==", + "deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "7.12.11", + "@eslint/eslintrc": "^0.4.3", + "@humanwhocodes/config-array": "^0.5.0", + "ajv": "^6.10.0", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.0.1", + "doctrine": "^3.0.0", + "enquirer": "^2.3.5", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^5.1.1", + "eslint-utils": "^2.1.0", + "eslint-visitor-keys": "^2.0.0", + "espree": "^7.3.1", + "esquery": "^1.4.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^6.0.1", + "functional-red-black-tree": "^1.0.1", + "glob-parent": "^5.1.2", + "globals": "^13.6.0", + "ignore": "^4.0.6", + "import-fresh": "^3.0.0", "imurmurhash": "^0.1.4", - "readable-stream": "1 || 2" + "is-glob": "^4.0.0", + "js-yaml": "^3.13.1", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.0.4", + "natural-compare": "^1.4.0", + "optionator": "^0.9.1", + "progress": "^2.0.0", + "regexpp": "^3.1.0", + "semver": "^7.2.1", + "strip-ansi": "^6.0.0", + "strip-json-comments": "^3.1.0", + "table": "^6.0.9", + "text-table": "^0.2.0", + "v8-compile-cache": "^2.0.3" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" } }, - "fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", - "dev": true + "node_modules/eslint-plugin-jsdoc": { + "version": "35.5.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-35.5.1.tgz", + "integrity": "sha512-pPYPWtsykwVEue1tYEyoppBj4dgF7XicF67tLLLraY6RQYBq7qMKjUHji19+hfiTtYKKBD0YfeK8hgjPAE5viw==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@es-joy/jsdoccomment": "0.9.0-alpha.1", + "comment-parser": "1.1.6-beta.0", + "debug": "^4.3.2", + "esquery": "^1.4.0", + "jsdoc-type-pratt-parser": "^1.0.4", + "lodash": "^4.17.21", + "regextras": "^0.8.0", + "semver": "^7.3.5", + "spdx-expression-parse": "^3.0.1" + }, + "engines": { + "node": ">=12" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0" + } }, - "fsevents": { - "version": "1.2.9", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.9.tgz", - "integrity": "sha512-oeyj2H3EjjonWcFjD5NvZNE9Rqe4UW+nQBU2HNeKw0koVLEFIhtyETyAakeAM3de7Z/SW5kcA+fZUait9EApnw==", + "node_modules/eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", "dev": true, - "optional": true, - "requires": { - "nan": "^2.12.1", - "node-pre-gyp": "^0.12.0" + "license": "BSD-2-Clause", + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/eslint-utils": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz", + "integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==", + "dev": true, + "license": "MIT", "dependencies": { - "abbrev": { - "version": "1.1.1", - "bundled": true, - "dev": true, - "optional": true - }, - "ansi-regex": { - "version": "2.1.1", - "bundled": true, - "dev": true, - "optional": true - }, - "aproba": { - "version": "1.2.0", - "bundled": true, - "dev": true, - "optional": true - }, - "are-we-there-yet": { - "version": "1.1.5", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "delegates": "^1.0.0", - "readable-stream": "^2.0.6" - } - }, - "balanced-match": { - "version": "1.0.0", - "bundled": true, - "dev": true, - "optional": true - }, - "brace-expansion": { - "version": "1.1.11", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "chownr": { - "version": "1.1.1", - "bundled": true, - "dev": true, - "optional": true - }, - "code-point-at": { - "version": "1.1.0", - "bundled": true, - "dev": true, - "optional": true - }, - "concat-map": { - "version": "0.0.1", - "bundled": true, - "dev": true, - "optional": true - }, - "console-control-strings": { - "version": "1.1.0", - "bundled": true, - "dev": true, - "optional": true - }, - "core-util-is": { - "version": "1.0.2", - "bundled": true, - "dev": true, - "optional": true - }, - "debug": { - "version": "4.1.1", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "ms": "^2.1.1" - } - }, - "deep-extend": { - "version": "0.6.0", - "bundled": true, - "dev": true, - "optional": true - }, - "delegates": { - "version": "1.0.0", - "bundled": true, - "dev": true, - "optional": true - }, - "detect-libc": { - "version": "1.0.3", - "bundled": true, - "dev": true, - "optional": true - }, - "fs-minipass": { - "version": "1.2.5", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "minipass": "^2.2.1" - } - }, - "fs.realpath": { - "version": "1.0.0", - "bundled": true, - "dev": true, - "optional": true - }, - "gauge": { - "version": "2.7.4", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "aproba": "^1.0.3", - "console-control-strings": "^1.0.0", - "has-unicode": "^2.0.0", - "object-assign": "^4.1.0", - "signal-exit": "^3.0.0", - "string-width": "^1.0.1", - "strip-ansi": "^3.0.1", - "wide-align": "^1.1.0" - } - }, - "glob": { - "version": "7.1.3", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "has-unicode": { - "version": "2.0.1", - "bundled": true, - "dev": true, - "optional": true - }, - "iconv-lite": { - "version": "0.4.24", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "safer-buffer": ">= 2.1.2 < 3" - } - }, - "ignore-walk": { - "version": "3.0.1", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "minimatch": "^3.0.4" - } - }, - "inflight": { - "version": "1.0.6", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "inherits": { - "version": "2.0.3", - "bundled": true, - "dev": true, - "optional": true - }, - "ini": { - "version": "1.3.5", - "bundled": true, - "dev": true, - "optional": true - }, - "is-fullwidth-code-point": { - "version": "1.0.0", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "number-is-nan": "^1.0.0" - } - }, - "isarray": { - "version": "1.0.0", - "bundled": true, - "dev": true, - "optional": true - }, - "minimatch": { - "version": "3.0.4", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "brace-expansion": "^1.1.7" - } - }, - "minimist": { - "version": "0.0.8", - "bundled": true, - "dev": true, - "optional": true - }, - "minipass": { - "version": "2.3.5", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "safe-buffer": "^5.1.2", - "yallist": "^3.0.0" - } - }, - "minizlib": { - "version": "1.2.1", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "minipass": "^2.2.1" - } - }, - "mkdirp": { - "version": "0.5.1", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "minimist": "0.0.8" - } - }, - "ms": { - "version": "2.1.1", - "bundled": true, - "dev": true, - "optional": true - }, - "needle": { - "version": "2.3.0", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "debug": "^4.1.0", - "iconv-lite": "^0.4.4", - "sax": "^1.2.4" - } - }, - "node-pre-gyp": { - "version": "0.12.0", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "detect-libc": "^1.0.2", - "mkdirp": "^0.5.1", - "needle": "^2.2.1", - "nopt": "^4.0.1", - "npm-packlist": "^1.1.6", - "npmlog": "^4.0.2", - "rc": "^1.2.7", - "rimraf": "^2.6.1", - "semver": "^5.3.0", - "tar": "^4" - } - }, - "nopt": { - "version": "4.0.1", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "abbrev": "1", - "osenv": "^0.1.4" - } - }, - "npm-bundled": { - "version": "1.0.6", - "bundled": true, - "dev": true, - "optional": true - }, - "npm-packlist": { - "version": "1.4.1", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "ignore-walk": "^3.0.1", - "npm-bundled": "^1.0.1" - } - }, - "npmlog": { - "version": "4.1.2", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "are-we-there-yet": "~1.1.2", - "console-control-strings": "~1.1.0", - "gauge": "~2.7.3", - "set-blocking": "~2.0.0" - } - }, - "number-is-nan": { - "version": "1.0.1", - "bundled": true, - "dev": true, - "optional": true - }, - "object-assign": { - "version": "4.1.1", - "bundled": true, - "dev": true, - "optional": true - }, - "once": { - "version": "1.4.0", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "wrappy": "1" - } - }, - "os-homedir": { - "version": "1.0.2", - "bundled": true, - "dev": true, - "optional": true - }, - "os-tmpdir": { - "version": "1.0.2", - "bundled": true, - "dev": true, - "optional": true - }, - "osenv": { - "version": "0.1.5", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "os-homedir": "^1.0.0", - "os-tmpdir": "^1.0.0" - } - }, - "path-is-absolute": { - "version": "1.0.1", - "bundled": true, - "dev": true, - "optional": true - }, - "process-nextick-args": { - "version": "2.0.0", - "bundled": true, - "dev": true, - "optional": true - }, - "rc": { - "version": "1.2.8", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "deep-extend": "^0.6.0", - "ini": "~1.3.0", - "minimist": "^1.2.0", - "strip-json-comments": "~2.0.1" - }, - "dependencies": { - "minimist": { - "version": "1.2.0", - "bundled": true, - "dev": true, - "optional": true - } - } - }, - "readable-stream": { - "version": "2.3.6", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "rimraf": { - "version": "2.6.3", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "glob": "^7.1.3" - } - }, - "safe-buffer": { - "version": "5.1.2", - "bundled": true, - "dev": true, - "optional": true - }, - "safer-buffer": { - "version": "2.1.2", - "bundled": true, - "dev": true, - "optional": true - }, - "sax": { - "version": "1.2.4", - "bundled": true, - "dev": true, - "optional": true - }, - "semver": { - "version": "5.7.0", - "bundled": true, - "dev": true, - "optional": true - }, - "set-blocking": { - "version": "2.0.0", - "bundled": true, - "dev": true, - "optional": true - }, - "signal-exit": { - "version": "3.0.2", - "bundled": true, - "dev": true, - "optional": true - }, - "string-width": { - "version": "1.0.2", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "code-point-at": "^1.0.0", - "is-fullwidth-code-point": "^1.0.0", - "strip-ansi": "^3.0.0" - } - }, - "string_decoder": { - "version": "1.1.1", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "safe-buffer": "~5.1.0" - } - }, - "strip-ansi": { - "version": "3.0.1", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "ansi-regex": "^2.0.0" - } - }, - "strip-json-comments": { - "version": "2.0.1", - "bundled": true, - "dev": true, - "optional": true - }, - "tar": { - "version": "4.4.8", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "chownr": "^1.1.1", - "fs-minipass": "^1.2.5", - "minipass": "^2.3.4", - "minizlib": "^1.1.1", - "mkdirp": "^0.5.0", - "safe-buffer": "^5.1.2", - "yallist": "^3.0.2" - } - }, - "util-deprecate": { - "version": "1.0.2", - "bundled": true, - "dev": true, - "optional": true - }, - "wide-align": { - "version": "1.1.3", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "string-width": "^1.0.2 || 2" - } - }, - "wrappy": { - "version": "1.0.2", - "bundled": true, - "dev": true, - "optional": true + "eslint-visitor-keys": "^1.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" + } + }, + "node_modules/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", + "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=4" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint/node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "license": "MIT", + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/eslint/node_modules/eslint-visitor-keys": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", + "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=10" + } + }, + "node_modules/eslint/node_modules/ignore": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", + "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/eslint/node_modules/js-yaml": { + "version": "3.14.2", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.2.tgz", + "integrity": "sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/espree": { + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-7.3.1.tgz", + "integrity": "sha512-v3JCNCE64umkFpmkFGqzVKsOT0tN1Zr+ueqLZfpV1Ob8e+CEgPWa+OxCoGH3tnhimMKIaBm4m/vaRpJ/krRz2g==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "acorn": "^7.4.0", + "acorn-jsx": "^5.3.1", + "eslint-visitor-keys": "^1.3.0" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/espree/node_modules/eslint-visitor-keys": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", + "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=4" + } + }, + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true, + "license": "BSD-2-Clause", + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/esquery": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.7.0.tgz", + "integrity": "sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esquery/node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esrecurse/node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/event-lite": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/event-lite/-/event-lite-0.1.3.tgz", + "integrity": "sha512-8qz9nOz5VeD2z96elrEKD2U433+L3DWdUdDkOINLGOJvx1GsMBbMn0aCeu28y8/e85A6mCigBiFlYMnTBEGlSw==", + "license": "MIT" + }, + "node_modules/execa": { + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-9.6.1.tgz", + "integrity": "sha512-9Be3ZoN4LmYR90tUoVu2te2BsbzHfhJyfEiAVfz7N5/zv+jduIfLrV2xdQXOHbaD6KgpGdO9PRPM1Y4Q9QkPkA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@sindresorhus/merge-streams": "^4.0.0", + "cross-spawn": "^7.0.6", + "figures": "^6.1.0", + "get-stream": "^9.0.0", + "human-signals": "^8.0.1", + "is-plain-obj": "^4.1.0", + "is-stream": "^4.0.1", + "npm-run-path": "^6.0.0", + "pretty-ms": "^9.2.0", + "signal-exit": "^4.1.0", + "strip-final-newline": "^4.0.0", + "yoctocolors": "^2.1.1" + }, + "engines": { + "node": "^18.19.0 || >=20.5.0" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-glob": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.8" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-uri": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.0.tgz", + "integrity": "sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" }, - "yallist": { - "version": "3.0.3", - "bundled": true, - "dev": true, - "optional": true + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" } - } + ], + "license": "BSD-3-Clause" }, - "fswin": { - "version": "3.18.918", - "resolved": "https://registry.npmjs.org/fswin/-/fswin-3.18.918.tgz", - "integrity": "sha512-9dwdStHWoL25DJiQ4jG/No9vBeKB+BVTLDuvXfNtVP5jf4j9zpBnje8gb5xpAqXN59wV7n8RSdDaGhhVzf7/ZA==" + "node_modules/fastq": { + "version": "1.20.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.20.1.tgz", + "integrity": "sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==", + "dev": true, + "license": "ISC", + "dependencies": { + "reusify": "^1.0.4" + } }, - "function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", - "dev": true + "node_modules/figures": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-6.1.0.tgz", + "integrity": "sha512-d+l3qxjSesT4V7v2fh+QnmFnUWv9lSpjarhShNTgBOfA0ttejbQUAlHLitbjkoRiDulW0OPoQPYIGhIC8ohejg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-unicode-supported": "^2.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } }, - "get-caller-file": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.3.tgz", - "integrity": "sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==", - "dev": true + "node_modules/file-entry-cache": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", + "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "dev": true, + "license": "MIT", + "dependencies": { + "flat-cache": "^3.0.4" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } }, - "get-stream": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", - "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=", - "dev": true + "node_modules/filelist": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.6.tgz", + "integrity": "sha512-5giy2PkLYY1cP39p17Ech+2xlpTRL9HLspOfEgm0L6CwBXBTgsK5ou0JtzYuepxkaQ/tvhCFIJ5uXo0OrM2DxA==", + "license": "Apache-2.0", + "dependencies": { + "minimatch": "^5.0.1" + } }, - "get-value": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz", - "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=", - "dev": true + "node_modules/filelist/node_modules/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } }, - "getpass": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", - "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", - "dev": true, - "requires": { - "assert-plus": "^1.0.0" + "node_modules/filelist/node_modules/minimatch": { + "version": "5.1.9", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.9.tgz", + "integrity": "sha512-7o1wEA2RyMP7Iu7GNba9vc0RWWGACJOCZBJX2GJWip0ikV+wcOsgVuY9uE8CPiyQhkGFSlhuSkZPavN7u1c2Fw==", + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" } }, - "glob": { - "version": "7.1.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz", - "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==", + "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==", "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" } }, - "glob-parent": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", - "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=", - "dev": true, - "requires": { - "is-glob": "^3.1.0", - "path-dirname": "^1.0.0" - }, - "dependencies": { - "is-glob": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", - "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", - "dev": true, - "requires": { - "is-extglob": "^2.1.0" - } - } + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "glob-parse": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/glob-parse/-/glob-parse-0.0.1.tgz", - "integrity": "sha1-N9Ml6xVTvpwfQnJzinVWZEWGbdY=", - "dev": true - }, - "global-dirs": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/global-dirs/-/global-dirs-0.1.1.tgz", - "integrity": "sha1-sxnA3UYH81PzvpzKTHL8FIxJ9EU=", + "node_modules/flat": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", + "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", "dev": true, - "requires": { - "ini": "^1.3.4" + "license": "BSD-3-Clause", + "bin": { + "flat": "cli.js" } }, - "global-modules": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-2.0.0.tgz", - "integrity": "sha512-NGbfmJBp9x8IxyJSd1P+otYK8vonoJactOogrVfFRIAEY1ukil8RSKDz2Yo7wh1oihl51l/r6W4epkeKJHqL8A==", - "dev": true, - "requires": { - "global-prefix": "^3.0.0" - }, - "dependencies": { - "global-prefix": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-3.0.0.tgz", - "integrity": "sha512-awConJSVCHVGND6x3tmMaKcQvwXLhjdkmomy2W+Goaui8YPgYgXJZewhg3fWC+DlfqqQuWg8AwqjGTD2nAPVWg==", - "dev": true, - "requires": { - "ini": "^1.3.5", - "kind-of": "^6.0.2", - "which": "^1.3.1" - } - }, - "which": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", - "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", - "dev": true, - "requires": { - "isexe": "^2.0.0" - } - } + "node_modules/flat-cache": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz", + "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==", + "dev": true, + "license": "MIT", + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.3", + "rimraf": "^3.0.2" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" } }, - "global-prefix": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-1.0.2.tgz", - "integrity": "sha1-2/dDxsFJklk8ZVVoy2btMsASLr4=", - "dev": true, - "requires": { - "expand-tilde": "^2.0.2", - "homedir-polyfill": "^1.0.1", - "ini": "^1.3.4", - "is-windows": "^1.0.1", - "which": "^1.2.14" - } - }, - "got": { - "version": "6.7.1", - "resolved": "https://registry.npmjs.org/got/-/got-6.7.1.tgz", - "integrity": "sha1-JAzQV4WpoY5WHcG0S0HHY+8ejbA=", - "dev": true, - "requires": { - "create-error-class": "^3.0.0", - "duplexer3": "^0.1.4", - "get-stream": "^3.0.0", - "is-redirect": "^1.0.0", - "is-retry-allowed": "^1.0.0", - "is-stream": "^1.0.0", - "lowercase-keys": "^1.0.0", - "safe-buffer": "^5.0.1", - "timed-out": "^4.0.0", - "unzip-response": "^2.0.1", - "url-parse-lax": "^1.0.0" - } - }, - "graceful-fs": { - "version": "4.1.15", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.15.tgz", - "integrity": "sha512-6uHUhOPEBgQ24HM+r6b/QwWfZq+yiFcipKFrOFiBEnWdy5sdzYoi+pJeQaPI5qOLRFqWmAXUPQNsielzdLoecA==" - }, - "growl": { - "version": "1.10.5", - "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz", - "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==", - "dev": true - }, - "har-schema": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", - "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=", - "dev": true + "node_modules/flatted": { + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.1.tgz", + "integrity": "sha512-IxfVbRFVlV8V/yRaGzk0UVIcsKKHMSfYw66T/u4nTwlWteQePsxe//LjudR1AMX4tZW3WFCh3Zqa/sjlqpbURQ==", + "dev": true, + "license": "ISC" }, - "har-validator": { - "version": "5.1.3", - "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.3.tgz", - "integrity": "sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g==", + "node_modules/foreground-child": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", + "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", "dev": true, - "requires": { - "ajv": "^6.5.5", - "har-schema": "^2.0.0" + "license": "ISC", + "dependencies": { + "cross-spawn": "^7.0.6", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "has": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", - "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "node_modules/form-data": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz", + "integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==", "dev": true, - "requires": { - "function-bind": "^1.1.1" + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.2", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" } }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", - "dev": true + "node_modules/fs-extra": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", + "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=12" + } }, - "has-symbols": { + "node_modules/fs.realpath": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.0.tgz", - "integrity": "sha1-uhqPGvKg/DllD1yFA2dwQSIGO0Q=", - "dev": true + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true, + "license": "ISC" }, - "has-value": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz", - "integrity": "sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=", + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", "dev": true, - "requires": { - "get-value": "^2.0.6", - "has-values": "^1.0.0", - "isobject": "^3.0.0" + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "has-values": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-values/-/has-values-1.0.0.tgz", - "integrity": "sha1-lbC2P+whRmGab+V/51Yo1aOe/k8=", - "dev": true, - "requires": { - "is-number": "^3.0.0", - "kind-of": "^4.0.0" - }, - "dependencies": { - "is-number": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", - "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "kind-of": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz", - "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } + "node_modules/functional-red-black-tree": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", + "integrity": "sha512-dsKNQNdj6xA3T+QlADDA7mOSlX0qiMINjn0cgr+eGHGsbSHzTabcIogz2+p/iqP1Xs6EP/sS2SbqH+brGTbq0g==", + "dev": true, + "license": "MIT" }, - "hash-base": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.0.4.tgz", - "integrity": "sha1-X8hoaEfs1zSZQDMZprCj8/auSRg=", + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", "dev": true, - "requires": { - "inherits": "^2.0.1", - "safe-buffer": "^5.0.1" + "license": "ISC", + "engines": { + "node": "6.* || 8.* || >= 10.*" } }, - "hash.js": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz", - "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==", + "node_modules/get-east-asian-width": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.5.0.tgz", + "integrity": "sha512-CQ+bEO+Tva/qlmw24dCejulK5pMzVnUOFOijVogd3KQs07HnRIgp8TGipvCCRT06xeYEbpbgwaCxglFyiuIcmA==", "dev": true, - "requires": { - "inherits": "^2.0.3", - "minimalistic-assert": "^1.0.1" + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "he": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", - "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", - "dev": true + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, - "hmac-drbg": { + "node_modules/get-proto": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", - "integrity": "sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", "dev": true, - "requires": { - "hash.js": "^1.0.3", - "minimalistic-assert": "^1.0.0", - "minimalistic-crypto-utils": "^1.0.1" + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" } }, - "homedir-polyfill": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/homedir-polyfill/-/homedir-polyfill-1.0.3.tgz", - "integrity": "sha512-eSmmWE5bZTK2Nou4g0AI3zZ9rswp7GRKoKXS1BLUkvPviOqs4YTN1djQIqrXy9k5gEtdLPy86JjRwsNM9tnDcA==", + "node_modules/get-stream": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-9.0.1.tgz", + "integrity": "sha512-kVCxPF3vQM/N0B1PmoqVUqgHP+EeVjmZSQn+1oCRPxd2P21P2F19lIgbR3HBosbB1PUhOAoctJnfEn2GbN2eZA==", "dev": true, - "requires": { - "parse-passwd": "^1.0.0" + "license": "MIT", + "dependencies": { + "@sec-ant/readable-stream": "^0.4.1", + "is-stream": "^4.0.1" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "http-proxy-agent": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-2.1.0.tgz", - "integrity": "sha512-qwHbBLV7WviBl0rQsOzH6o5lwyOIvwp/BdFnvVxXORldu5TmjFfjzBcWUWS5kWAZhmv+JtiDhSuQCp4sBfbIgg==", - "dev": true, - "requires": { - "agent-base": "4", - "debug": "3.1.0" - }, - "dependencies": { - "debug": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true - } + "node_modules/git-cliff": { + "version": "2.12.0", + "resolved": "https://registry.npmjs.org/git-cliff/-/git-cliff-2.12.0.tgz", + "integrity": "sha512-kjTm5439LsvMs/xRxndWBUetrA4aQfLE8DTbR/ER5H7fGn7ioeFG9YNAK1V7dpTtNi6k2uKYY4f3EvT8J1d+1Q==", + "dev": true, + "license": "MIT OR Apache-2.0", + "dependencies": { + "execa": "^9.6.0" + }, + "bin": { + "git-cliff": "lib/cli/cli.js" + }, + "engines": { + "node": ">=18.19 || >=20.6 || >=21" + }, + "optionalDependencies": { + "git-cliff-darwin-arm64": "2.12.0", + "git-cliff-darwin-x64": "2.12.0", + "git-cliff-linux-arm64": "2.12.0", + "git-cliff-linux-x64": "2.12.0", + "git-cliff-windows-arm64": "2.12.0", + "git-cliff-windows-x64": "2.12.0" + } + }, + "node_modules/git-cliff-darwin-arm64": { + "version": "2.12.0", + "resolved": "https://registry.npmjs.org/git-cliff-darwin-arm64/-/git-cliff-darwin-arm64-2.12.0.tgz", + "integrity": "sha512-k3jzFDmkjc+6MjpnqvRenzMWRbZN5J+w3iQ8WNt9pSmPewNJIm92O/G6AbAxQaCbSfzQapeZ0e+5wSacVc62GA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT OR Apache-2.0", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/git-cliff-darwin-x64": { + "version": "2.12.0", + "resolved": "https://registry.npmjs.org/git-cliff-darwin-x64/-/git-cliff-darwin-x64-2.12.0.tgz", + "integrity": "sha512-Kkoe+nfmXM/WMcZuC+OaIGA5vj847Ima6NEaaHnyb7Xsri+OAJryPXlABV7q6UeGfiiN2MlL8UsoHgnIEIQLqQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT OR Apache-2.0", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/git-cliff-linux-arm64": { + "version": "2.12.0", + "resolved": "https://registry.npmjs.org/git-cliff-linux-arm64/-/git-cliff-linux-arm64-2.12.0.tgz", + "integrity": "sha512-eTp2gZjV4LmfzdlhFsYFYuWf5mojALU03X/37r3VmnpuabaijuTEQo/zm/0BKP8gPiLKLR4ofdUvE1OSisCE1A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT OR Apache-2.0", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/git-cliff-linux-x64": { + "version": "2.12.0", + "resolved": "https://registry.npmjs.org/git-cliff-linux-x64/-/git-cliff-linux-x64-2.12.0.tgz", + "integrity": "sha512-abidFG6dH2N5hPUF245/kRYdwViP11Pz7ZwIW/a86CJLZ/WSE7dJt0f2cUIkxTcFSsp11OwuLc5k1hAbwmiIRw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT OR Apache-2.0", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/git-cliff-windows-arm64": { + "version": "2.12.0", + "resolved": "https://registry.npmjs.org/git-cliff-windows-arm64/-/git-cliff-windows-arm64-2.12.0.tgz", + "integrity": "sha512-rFuI+D/3Yq3jqafazZw5E68HsXEvcwI/B/5IPDIZD+QqZh8vETf4IXs7wVxYWWtHQJDC+G9ZrR3vE5648mdG3A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT OR Apache-2.0", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/git-cliff-windows-x64": { + "version": "2.12.0", + "resolved": "https://registry.npmjs.org/git-cliff-windows-x64/-/git-cliff-windows-x64-2.12.0.tgz", + "integrity": "sha512-jskb3nyVGr4dekHSCDM/J6iho45t37wnmMGkPNq42kOoUp04JS96yMBrNRdXfXV9ViZsaZq3NaNu1e3QkhFlyA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT OR Apache-2.0", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/glob": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", + "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", + "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", + "dev": true, + "license": "ISC", + "dependencies": { + "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" } }, - "http-signature": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", - "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", + "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==", "dev": true, - "requires": { - "assert-plus": "^1.0.0", - "jsprim": "^1.2.2", - "sshpk": "^1.7.0" + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" } }, - "https-browserify": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/https-browserify/-/https-browserify-1.0.0.tgz", - "integrity": "sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM=", - "dev": true - }, - "https-proxy-agent": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-2.2.3.tgz", - "integrity": "sha512-Ytgnz23gm2DVftnzqRRz2dOXZbGd2uiajSw/95bPp6v53zPRspQjLm/AfBgqbJ2qfeRXWIOMVLpp86+/5yX39Q==", - "dev": true, - "requires": { - "agent-base": "^4.3.0", - "debug": "^3.1.0" - }, - "dependencies": { - "agent-base": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-4.3.0.tgz", - "integrity": "sha512-salcGninV0nPrwpGNn4VTXBb1SOuXQBiqbrNXoeizJsHrsL6ERFM2Ne3JUSBWRE6aeNJI2ROP/WEEIDUiDe3cg==", - "dev": true, - "requires": { - "es6-promisify": "^5.0.0" - } - } + "node_modules/glob/node_modules/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" } }, - "ieee754": { - "version": "1.1.13", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz", - "integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg==", - "dev": true + "node_modules/glob/node_modules/minimatch": { + "version": "9.0.9", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz", + "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.2" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } }, - "iferr": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/iferr/-/iferr-0.1.5.tgz", - "integrity": "sha1-xg7taebY/bazEEofy8ocGS3FtQE=", - "dev": true + "node_modules/globals": { + "version": "13.24.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", + "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } }, - "import-lazy": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/import-lazy/-/import-lazy-2.1.0.tgz", - "integrity": "sha1-BWmOPUXIjo1+nZLLBYTnfwlvPkM=", - "dev": true + "node_modules/globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } }, - "import-local": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/import-local/-/import-local-2.0.0.tgz", - "integrity": "sha512-b6s04m3O+s3CGSbqDIyP4R6aAwAeYlVq9+WUWep6iHa8ETRf9yei1U48C5MmfJmV9AiLYYBKPMq/W+/WRpQmCQ==", + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", "dev": true, - "requires": { - "pkg-dir": "^3.0.0", - "resolve-cwd": "^2.0.0" + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", - "dev": true + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "license": "ISC" }, - "infer-owner": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/infer-owner/-/infer-owner-1.0.4.tgz", - "integrity": "sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A==", - "dev": true + "node_modules/graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", + "dev": true, + "license": "MIT" }, - "inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "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==", "dev": true, - "requires": { - "once": "^1.3.0", - "wrappy": "1" + "license": "MIT", + "engines": { + "node": ">=8" } }, - "inherits": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", - "dev": true + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, - "ini": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz", - "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==", - "dev": true + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, - "interpret": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.2.0.tgz", - "integrity": "sha512-mT34yGKMNceBQUoVn7iCDKDntA7SC6gycMAWzGx1z/CMCTV7b2AAtXlo3nRyHZ1FelRkQbQjprHSYGwzLtkVbw==", - "dev": true + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } }, - "invert-kv": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-2.0.0.tgz", - "integrity": "sha512-wPVv/y/QQ/Uiirj/vh3oP+1Ww+AWehmi1g5fFWGPF6IpCBCDVrhgHRMvrLfdYcwDh3QJbGXDW4JAuzxElLSqKA==", - "dev": true + "node_modules/he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", + "dev": true, + "license": "MIT", + "bin": { + "he": "bin/he" + } + }, + "node_modules/highlight.js": { + "version": "11.11.1", + "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-11.11.1.tgz", + "integrity": "sha512-Xwwo44whKBVCYoliBQwaPvtd/2tYFkRQtXDWj1nackaV2JPXx3L0+Jvd8/qCJ2p+ML0/XVkJ2q+Mr+UVdpJK5w==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/htmlparser2": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-8.0.2.tgz", + "integrity": "sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA==", + "funding": [ + "https://github.com/fb55/htmlparser2?sponsor=1", + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "license": "MIT", + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3", + "domutils": "^3.0.1", + "entities": "^4.4.0" + } }, - "is-accessor-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", - "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", + "node_modules/http-proxy-agent": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", + "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, + "license": "MIT", "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } + "agent-base": "^7.1.0", + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" } }, - "is-arrayish": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", - "dev": true + "node_modules/httpgd": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/httpgd/-/httpgd-0.1.6.tgz", + "integrity": "sha512-HyozzYjOq+rGi3P+YZtLnvBPAWvdn2tiCfUuB4tSUradRtOoKAvwcZ+yvOYxusMzaZIGkf02s/BTkcDzj+XS/w==", + "license": "MIT", + "dependencies": { + "@types/ws": "^8.2.0", + "cross-fetch": "^3.1.4", + "isomorphic-ws": "^4.0.1", + "ws": "^8.2.3" + } }, - "is-binary-path": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz", - "integrity": "sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg=", + "node_modules/https-proxy-agent": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", + "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", "dev": true, - "requires": { - "binary-extensions": "^1.0.0" + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" } }, - "is-buffer": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", - "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", - "dev": true + "node_modules/human-signals": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-8.0.1.tgz", + "integrity": "sha512-eKCa6bwnJhvxj14kZk5NCPc6Hb6BdsU9DZcOnmQKSnO1VKrfV0zCvtttPZUsBvjmNDn8rpcJfpwSYnHBjc95MQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18.0" + } }, - "is-callable": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.4.tgz", - "integrity": "sha512-r5p9sxJjYnArLjObpjA4xu5EKI3CuKHkJXMhT7kwbpUyIFD1n5PMAsoPvWnvtZiNz7LjkYDRZhd7FlI0eMijEA==", - "dev": true + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "BSD-3-Clause" }, - "is-ci": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-1.1.0.tgz", - "integrity": "sha512-c7TnwxLePuqIlxHgr7xtxzycJPegNHFuIrBkwbf8hc58//+Op1CqFkyS+xnIMkwn9UsJIwc174BIjkyBmSpjKg==", + "node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", "dev": true, - "requires": { - "ci-info": "^1.0.0" + "license": "MIT", + "engines": { + "node": ">= 4" } }, - "is-data-descriptor": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", - "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", + "node_modules/immediate": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz", + "integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==", "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, + "license": "MIT" + }, + "node_modules/import-fresh": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", + "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", + "dev": true, + "license": "MIT", "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "is-date-object": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.1.tgz", - "integrity": "sha1-mqIOtq7rv/d/vTPnTKAbM1gdOhY=", - "dev": true + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } }, - "is-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", - "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", "dev": true, - "requires": { - "is-accessor-descriptor": "^0.1.6", - "is-data-descriptor": "^0.1.4", - "kind-of": "^5.0.0" - }, + "license": "ISC", "dependencies": { - "kind-of": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", - "dev": true - } + "once": "^1.3.0", + "wrappy": "1" } }, - "is-extendable": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", - "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", - "dev": true + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/int64-buffer": { + "version": "0.1.10", + "resolved": "https://registry.npmjs.org/int64-buffer/-/int64-buffer-0.1.10.tgz", + "integrity": "sha512-v7cSY1J8ydZ0GyjUHqF+1bshJ6cnEVLo9EnjB8p+4HDRPZc9N5jjmvUV7NvEsqQOKyH0pmIBFWXVQbiS0+OBbA==", + "license": "MIT" }, - "is-extglob": { + "node_modules/is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", - "dev": true + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } }, - "is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", - "dev": true + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } }, - "is-glob": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", - "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", + "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==", "dev": true, - "requires": { + "license": "MIT", + "dependencies": { "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" } }, - "is-installed-globally": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/is-installed-globally/-/is-installed-globally-0.1.0.tgz", - "integrity": "sha1-Df2Y9akRFxbdU13aZJL2e/PSWoA=", + "node_modules/is-interactive": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-2.0.0.tgz", + "integrity": "sha512-qP1vozQRI+BMOPcjFzrjXuQvdak2pHNUMZoeG2eRbiSqyvbEf/wQtEOTOX1guk6E3t36RkaqiSt8A/6YElNxLQ==", "dev": true, - "requires": { - "global-dirs": "^0.1.0", - "is-path-inside": "^1.0.0" + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "is-npm": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-npm/-/is-npm-1.0.0.tgz", - "integrity": "sha1-8vtjpl5JBbQGyGBydloaTceTufQ=", - "dev": true - }, - "is-number": { + "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==", - "dev": true - }, - "is-obj": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz", - "integrity": "sha1-PkcprB9f3gJc19g6iW2rn09n2w8=", - "dev": true - }, - "is-path-inside": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-1.0.1.tgz", - "integrity": "sha1-jvW33lBDej/cprToZe96pVy0gDY=", "dev": true, - "requires": { - "path-is-inside": "^1.0.1" + "license": "MIT", + "engines": { + "node": ">=0.12.0" } }, - "is-plain-object": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", - "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", + "node_modules/is-path-inside": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", + "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", "dev": true, - "requires": { - "isobject": "^3.0.1" + "license": "MIT", + "engines": { + "node": ">=8" } }, - "is-redirect": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-redirect/-/is-redirect-1.0.0.tgz", - "integrity": "sha1-HQPd7VO9jbDzDCbk+V02/HyH3CQ=", - "dev": true - }, - "is-regex": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.4.tgz", - "integrity": "sha1-VRdIm1RwkbCTDglWVM7SXul+lJE=", + "node_modules/is-plain-obj": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-4.1.0.tgz", + "integrity": "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==", "dev": true, - "requires": { - "has": "^1.0.1" + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "is-retry-allowed": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-retry-allowed/-/is-retry-allowed-1.1.0.tgz", - "integrity": "sha1-EaBgVotnM5REAz0BJaYaINVk+zQ=", - "dev": true - }, - "is-stream": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", - "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=", - "dev": true - }, - "is-symbol": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.2.tgz", - "integrity": "sha512-HS8bZ9ox60yCJLH9snBpIwv9pYUAkcuLhSA1oero1UB5y9aiQpRA8y2ex945AOtCZL1lJDeIk3G5LthswI46Lw==", + "node_modules/is-stream": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-4.0.1.tgz", + "integrity": "sha512-Dnz92NInDqYckGEUJv689RbRiTSEHCQ7wOVeALbkOz999YpqT46yMRIGtSNl2iCL1waAZSx40+h59NV/EwzV/A==", "dev": true, - "requires": { - "has-symbols": "^1.0.0" + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "is-typedarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", - "dev": true - }, - "is-windows": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", - "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", - "dev": true - }, - "is-wsl": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-1.1.0.tgz", - "integrity": "sha1-HxbkqiKwTRM2tmGIpmrzxgDDpm0=", - "dev": true + "node_modules/is-unicode-supported": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-2.1.0.tgz", + "integrity": "sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } }, - "isarray": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", - "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", - "dev": true + "node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "license": "MIT" }, - "isexe": { + "node_modules/isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", - "dev": true + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true, + "license": "ISC" }, - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", - "dev": true + "node_modules/isomorphic-ws": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/isomorphic-ws/-/isomorphic-ws-4.0.1.tgz", + "integrity": "sha512-BhBvN2MBpWTaSHdWRb/bwdZJ1WaehQ2L1KngkCkfLUGF0mAWAT1sQUQacEmQ0jXkFw/czDXPNQSL5u2/Krsz1w==", + "license": "MIT", + "peerDependencies": { + "ws": "*" + } + }, + "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, + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, + "node_modules/jake": { + "version": "10.9.4", + "resolved": "https://registry.npmjs.org/jake/-/jake-10.9.4.tgz", + "integrity": "sha512-wpHYzhxiVQL+IV05BLE2Xn34zW1S223hvjtqk0+gsPrwd/8JNLXJgZZM/iPFsYc1xyphF+6M6EvdE5E9MBGkDA==", + "license": "Apache-2.0", + "dependencies": { + "async": "^3.2.6", + "filelist": "^1.0.4", + "picocolors": "^1.1.1" + }, + "bin": { + "jake": "bin/cli.js" + }, + "engines": { + "node": ">=10" + } }, - "isstream": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", - "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=", - "dev": true + "node_modules/jquery": { + "version": "3.7.1", + "resolved": "https://registry.npmjs.org/jquery/-/jquery-3.7.1.tgz", + "integrity": "sha512-m4avr8yL8kmFN8psrbFFFmB/If14iN5o9nw/NgnnM+kybDJpRsAynV2BsfpTYrTRysYUdADVD7CkUUizgkpLfg==", + "license": "MIT" + }, + "node_modules/jquery.json-viewer": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/jquery.json-viewer/-/jquery.json-viewer-1.5.0.tgz", + "integrity": "sha512-M/mRFXg14V/UUAlz7TBNBIDmQdWt05BunsqC/UjEx5BoFdQpNpfkfDdVn+VtjX951n/an/T9GWB3apBp02x8Mg==", + "license": "MIT" }, - "js-tokens": { + "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "dev": true - }, - "js-yaml": { - "version": "3.13.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz", - "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==", "dev": true, - "requires": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - } + "license": "MIT" }, - "jsbn": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", - "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=", - "dev": true + "node_modules/js-yaml": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", + "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } }, - "json-parse-better-errors": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", - "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", - "dev": true + "node_modules/jsdoc-type-pratt-parser": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/jsdoc-type-pratt-parser/-/jsdoc-type-pratt-parser-1.2.0.tgz", + "integrity": "sha512-4STjeF14jp4bqha44nKMY1OUI6d2/g6uclHWUCZ7B4DoLzaB5bmpTkQrpqU+vSVzMD0LsKAOskcnI3I3VfIpmg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + } }, - "json-schema": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", - "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=", - "dev": true + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true, + "license": "MIT" }, - "json-schema-traverse": { + "node_modules/json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true - }, - "json-stringify-safe": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", - "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=", - "dev": true + "dev": true, + "license": "MIT" }, - "json5": { + "node_modules/json-stable-stringify-without-jsonify": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", - "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", "dev": true, - "requires": { - "minimist": "^1.2.0" - } + "license": "MIT" }, - "jsonfile": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", - "integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=", - "requires": { + "node_modules/jsonfile": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.0.tgz", + "integrity": "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==", + "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { "graceful-fs": "^4.1.6" } }, - "jsprim": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", - "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", + "node_modules/jszip": { + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.10.1.tgz", + "integrity": "sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==", "dev": true, - "requires": { - "assert-plus": "1.0.0", - "extsprintf": "1.3.0", - "json-schema": "0.2.3", - "verror": "1.10.0" + "license": "(MIT OR GPL-3.0-or-later)", + "dependencies": { + "lie": "~3.3.0", + "pako": "~1.0.2", + "readable-stream": "~2.3.6", + "setimmediate": "^1.0.5" } }, - "kind-of": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", - "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", - "dev": true + "node_modules/just-extend": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/just-extend/-/just-extend-6.2.0.tgz", + "integrity": "sha512-cYofQu2Xpom82S6qD778jBDpwvvy39s1l/hrYij2u9AMdQcGRpaBu6kY4mVhuno5kJVi1DAz4aiphA2WI1/OAw==", + "dev": true, + "license": "MIT" }, - "latest-version": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/latest-version/-/latest-version-3.1.0.tgz", - "integrity": "sha1-ogU4P+oyKzO1rjsYq+4NwvNW7hU=", + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", "dev": true, - "requires": { - "package-json": "^4.0.0" + "license": "MIT", + "dependencies": { + "json-buffer": "3.0.1" } }, - "lcid": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/lcid/-/lcid-2.0.0.tgz", - "integrity": "sha512-avPEb8P8EGnwXKClwsNUgryVjllcRqtMYa49NTsbQagYuT1DcXnl1915oxWjoyGrXR6zH/Y0Zc96xWsPcoDKeA==", + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", "dev": true, - "requires": { - "invert-kv": "^2.0.0" + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" } }, - "loader-runner": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-2.4.0.tgz", - "integrity": "sha512-Jsmr89RcXGIwivFY21FcRrisYZfvLMTWx5kOLc+JTxtpBOG6xML0vzbc6SEQG2FO9/4Fc3wW4LVcB5DmGflaRw==", - "dev": true - }, - "loader-utils": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.2.3.tgz", - "integrity": "sha512-fkpz8ejdnEMG3s37wGL07iSBDg99O9D5yflE9RGNH3hRdx9SOwYfnGYdZOUIZitN8E+E2vkq3MUMYMvPYl5ZZA==", + "node_modules/lie": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz", + "integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==", "dev": true, - "requires": { - "big.js": "^5.2.2", - "emojis-list": "^2.0.0", - "json5": "^1.0.1" + "license": "MIT", + "dependencies": { + "immediate": "~3.0.5" } }, - "locate-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", - "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", "dev": true, - "requires": { - "p-locate": "^3.0.0", - "path-exists": "^3.0.0" + "license": "MIT", + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "lodash": { - "version": "4.17.15", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", - "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==", - "dev": true + "node_modules/lodash": { + "version": "4.17.23", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.23.tgz", + "integrity": "sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w==", + "dev": true, + "license": "MIT" }, - "log-symbols": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-2.2.0.tgz", - "integrity": "sha512-VeIAFslyIerEJLXHziedo2basKbMKtTw3vfn5IzG0XTjhAVEJyNHnL2p7vc+wBDSdQuUpNw3M2u6xb9QsAY5Eg==", + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", "dev": true, - "requires": { - "chalk": "^2.0.1" - } + "license": "MIT" }, - "lowercase-keys": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz", - "integrity": "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==", - "dev": true + "node_modules/lodash.truncate": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz", + "integrity": "sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw==", + "dev": true, + "license": "MIT" }, - "lru-cache": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.2.tgz", - "integrity": "sha512-wgeVXhrDwAWnIF/yZARsFnMBtdFXOg1b8RIrhilp+0iDYN4mdQcNZElDZ0e4B64BhaxeQ5zN7PMyvu7we1kPeQ==", + "node_modules/log-symbols": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", + "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", "dev": true, - "requires": { - "pseudomap": "^1.0.2", - "yallist": "^2.1.2" + "license": "MIT", + "dependencies": { + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "make-dir": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-1.2.0.tgz", - "integrity": "sha512-aNUAa4UMg/UougV25bbrU4ZaaKNjJ/3/xnvg/twpmKROPdKZPZ9wGgI0opdZzO8q/zUFawoUuixuOv33eZ61Iw==", + "node_modules/log-symbols/node_modules/is-unicode-supported": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", + "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", "dev": true, - "requires": { - "pify": "^3.0.0" + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "mamacro": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/mamacro/-/mamacro-0.0.3.tgz", - "integrity": "sha512-qMEwh+UujcQ+kbz3T6V+wAmO2U8veoq2w+3wY8MquqwVA3jChfwY+Tk52GZKDfACEPjuZ7r2oJLejwpt8jtwTA==", - "dev": true + "node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true, + "license": "ISC" }, - "map-age-cleaner": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/map-age-cleaner/-/map-age-cleaner-0.1.3.tgz", - "integrity": "sha512-bJzx6nMoP6PDLPBFmg7+xRKeFZvFboMrGlxmNj9ClvX53KrmvM5bXFXEWjbz4cz1AFn+jWJ9z/DJSz7hrs0w3w==", + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", "dev": true, - "requires": { - "p-defer": "^1.0.0" + "license": "MIT", + "engines": { + "node": ">= 0.4" } }, - "map-cache": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", - "integrity": "sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=", - "dev": true - }, - "map-visit": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz", - "integrity": "sha1-7Nyo8TFE5mDxtb1B8S80edmN+48=", + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", "dev": true, - "requires": { - "object-visit": "^1.0.0" + "license": "MIT", + "engines": { + "node": ">= 8" } }, - "md5.js": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz", - "integrity": "sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==", + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", "dev": true, - "requires": { - "hash-base": "^3.0.0", - "inherits": "^2.0.1", - "safe-buffer": "^5.1.2" + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" } }, - "mem": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/mem/-/mem-4.3.0.tgz", - "integrity": "sha512-qX2bG48pTqYRVmDB37rn/6PT7LcR8T7oAX3bf99u1Tt1nzxYfxkgqDwUwolPlXweM0XzBOBFzSx4kfp7KP1s/w==", + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", "dev": true, - "requires": { - "map-age-cleaner": "^0.1.1", - "mimic-fn": "^2.0.0", - "p-is-promise": "^2.0.0" + "license": "MIT", + "engines": { + "node": ">= 0.6" } }, - "memory-fs": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.4.1.tgz", - "integrity": "sha1-OpoguEYlI+RHz7x+i7gO1me/xVI=", + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", "dev": true, - "requires": { - "errno": "^0.1.3", - "readable-stream": "^2.0.1" - }, + "license": "MIT", "dependencies": { - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", - "dev": true - }, - "readable-stream": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", - "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", - "dev": true, - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dev": true, - "requires": { - "safe-buffer": "~5.1.0" - } - } + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" } }, - "microee": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/microee/-/microee-0.0.6.tgz", - "integrity": "sha1-oSvbAQNoHosSapsHHrpMRnx4//4=", - "dev": true - }, - "micromatch": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.2.tgz", - "integrity": "sha512-y7FpHSbMUMoyPbYUSzO6PaZ6FyRnQOpHuKwbo1G+Knck95XVU4QAiKdGEnj5wwoS7PlOgthX/09u5iFJ+aYf5Q==", + "node_modules/mimic-function": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/mimic-function/-/mimic-function-5.0.1.tgz", + "integrity": "sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==", "dev": true, - "requires": { - "braces": "^3.0.1", - "picomatch": "^2.0.5" + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "miller-rabin": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/miller-rabin/-/miller-rabin-4.0.1.tgz", - "integrity": "sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA==", + "node_modules/minimatch": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", "dev": true, - "requires": { - "bn.js": "^4.0.0", - "brorand": "^1.0.1" + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" } }, - "mime-db": { - "version": "1.38.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.38.0.tgz", - "integrity": "sha512-bqVioMFFzc2awcdJZIzR3HjZFX20QhilVS7hytkKrv7xFAn8bM1gzc/FOX2awLISvWe0PV8ptFKcon+wZ5qYkg==", - "dev": true + "node_modules/minipass": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.3.tgz", + "integrity": "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=16 || 14 >=14.17" + } }, - "mime-types": { - "version": "2.1.22", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.22.tgz", - "integrity": "sha512-aGl6TZGnhm/li6F7yx82bJiBZwgiEa4Hf6CNr8YO+r5UHr53tSTYZb102zyU50DOWWKeOv0uQLRL0/9EiKWCog==", + "node_modules/mocha": { + "version": "11.7.5", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-11.7.5.tgz", + "integrity": "sha512-mTT6RgopEYABzXWFx+GcJ+ZQ32kp4fMf0xvpZIIfSq9Z8lC/++MtcCnQ9t5FP2veYEP95FIYSvW+U9fV4xrlig==", "dev": true, - "requires": { - "mime-db": "~1.38.0" + "license": "MIT", + "dependencies": { + "browser-stdout": "^1.3.1", + "chokidar": "^4.0.1", + "debug": "^4.3.5", + "diff": "^7.0.0", + "escape-string-regexp": "^4.0.0", + "find-up": "^5.0.0", + "glob": "^10.4.5", + "he": "^1.2.0", + "is-path-inside": "^3.0.3", + "js-yaml": "^4.1.0", + "log-symbols": "^4.1.0", + "minimatch": "^9.0.5", + "ms": "^2.1.3", + "picocolors": "^1.1.1", + "serialize-javascript": "^6.0.2", + "strip-json-comments": "^3.1.1", + "supports-color": "^8.1.1", + "workerpool": "^9.2.0", + "yargs": "^17.7.2", + "yargs-parser": "^21.1.1", + "yargs-unparser": "^2.0.0" + }, + "bin": { + "_mocha": "bin/_mocha", + "mocha": "bin/mocha.js" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, - "mimic-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", - "dev": true + "node_modules/mocha/node_modules/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } }, - "minimalistic-assert": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", - "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==", - "dev": true + "node_modules/mocha/node_modules/minimatch": { + "version": "9.0.9", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz", + "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.2" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } }, - "minimalistic-crypto-utils": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", - "integrity": "sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=", - "dev": true + "node_modules/mocha/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } }, - "minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "dev": true, - "requires": { - "brace-expansion": "^1.1.7" + "license": "MIT" + }, + "node_modules/msgpack-lite": { + "version": "0.1.26", + "resolved": "https://registry.npmjs.org/msgpack-lite/-/msgpack-lite-0.1.26.tgz", + "integrity": "sha512-SZ2IxeqZ1oRFGo0xFGbvBJWMp3yLIY9rlIJyxy8CGrwZn1f0ZK4r6jV/AM1r0FZMDUkWkglOk/eeKIL9g77Nxw==", + "license": "MIT", + "dependencies": { + "event-lite": "^0.1.1", + "ieee754": "^1.1.8", + "int64-buffer": "^0.1.9", + "isarray": "^1.0.0" + }, + "bin": { + "msgpack": "bin/msgpack" } }, - "minimist": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", - "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", - "dev": true + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true, + "license": "MIT" }, - "miniq": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/miniq/-/miniq-1.0.1.tgz", - "integrity": "sha1-hV7SmtMZRW8IeFobTwa7vdmk8sE=", + "node_modules/natural-compare-lite": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare-lite/-/natural-compare-lite-1.4.0.tgz", + "integrity": "sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==", "dev": true, - "requires": { - "microee": "0.0.6", - "ondone": "~1.0.0" - } + "license": "MIT" }, - "mississippi": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/mississippi/-/mississippi-3.0.0.tgz", - "integrity": "sha512-x471SsVjUtBRtcvd4BzKE9kFC+/2TeWgKCgw0bZcw1b9l2X3QX5vCWgF+KaZaYm87Ss//rHnWryupDrgLvmSkA==", - "dev": true, - "requires": { - "concat-stream": "^1.5.0", - "duplexify": "^3.4.2", - "end-of-stream": "^1.1.0", - "flush-write-stream": "^1.0.0", - "from2": "^2.1.0", - "parallel-transform": "^1.1.0", - "pump": "^3.0.0", - "pumpify": "^1.3.3", - "stream-each": "^1.1.0", - "through2": "^2.0.0" - }, - "dependencies": { - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", - "dev": true - }, - "readable-stream": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", - "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", - "dev": true, - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dev": true, - "requires": { - "safe-buffer": "~5.1.0" - } - }, - "through2": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", - "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", - "dev": true, - "requires": { - "readable-stream": "~2.3.6", - "xtend": "~4.0.1" - } - } + "node_modules/nerdbank-streams": { + "version": "2.5.60", + "resolved": "https://registry.npmjs.org/nerdbank-streams/-/nerdbank-streams-2.5.60.tgz", + "integrity": "sha512-saQaMyTtVDAEc+S+BPXKM6K1AF3FyrorFSDzaCkdmtDe2kZzu1aYPQZNLmnxJhxbTcghYrEmYFFoaDxBDVadCw==", + "license": "MIT", + "dependencies": { + "await-semaphore": "^0.1.3", + "cancellationtoken": "^2.0.1", + "caught": "^0.1.3", + "msgpack-lite": "^0.1.26" } }, - "mixin-deep": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz", - "integrity": "sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==", + "node_modules/nise": { + "version": "5.1.9", + "resolved": "https://registry.npmjs.org/nise/-/nise-5.1.9.tgz", + "integrity": "sha512-qOnoujW4SV6e40dYxJOb3uvuoPHtmLzIk4TFo+j0jPJoC+5Z9xja5qH5JZobEPsa8+YYphMrOSwnrshEhG2qww==", "dev": true, - "requires": { - "for-in": "^1.0.2", - "is-extendable": "^1.0.1" - }, + "license": "BSD-3-Clause", "dependencies": { - "is-extendable": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", - "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", - "dev": true, - "requires": { - "is-plain-object": "^2.0.4" - } - } + "@sinonjs/commons": "^3.0.0", + "@sinonjs/fake-timers": "^11.2.2", + "@sinonjs/text-encoding": "^0.7.2", + "just-extend": "^6.2.0", + "path-to-regexp": "^6.2.1" } }, - "mkdirp": { - "version": "0.5.1", - "resolved": "http://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", - "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", + "node_modules/nise/node_modules/@sinonjs/fake-timers": { + "version": "11.3.1", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-11.3.1.tgz", + "integrity": "sha512-EVJO7nW5M/F5Tur0Rf2z/QoMo+1Ia963RiMtapiQrEWvY0iBUvADo8Beegwjpnle5BHkyHuoxSTW3jF43H1XRA==", "dev": true, - "requires": { - "minimist": "0.0.8" - }, + "license": "BSD-3-Clause", "dependencies": { - "minimist": { - "version": "0.0.8", - "resolved": "http://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", - "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", - "dev": true - } + "@sinonjs/commons": "^3.0.1" } }, - "mm-brace-expand": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/mm-brace-expand/-/mm-brace-expand-0.0.1.tgz", - "integrity": "sha1-jrbgqZ+HrTT/8YJzMRBJadjFBP4=", - "dev": true - }, - "mocha": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-6.2.0.tgz", - "integrity": "sha512-qwfFgY+7EKAAUAdv7VYMZQknI7YJSGesxHyhn6qD52DV8UcSZs5XwCifcZGMVIE4a5fbmhvbotxC0DLQ0oKohQ==", - "dev": true, - "requires": { - "ansi-colors": "3.2.3", - "browser-stdout": "1.3.1", - "debug": "3.2.6", - "diff": "3.5.0", - "escape-string-regexp": "1.0.5", - "find-up": "3.0.0", - "glob": "7.1.3", - "growl": "1.10.5", - "he": "1.2.0", - "js-yaml": "3.13.1", - "log-symbols": "2.2.0", - "minimatch": "3.0.4", - "mkdirp": "0.5.1", - "ms": "2.1.1", - "node-environment-flags": "1.0.5", - "object.assign": "4.1.0", - "strip-json-comments": "2.0.1", - "supports-color": "6.0.0", - "which": "1.3.1", - "wide-align": "1.1.3", - "yargs": "13.2.2", - "yargs-parser": "13.0.0", - "yargs-unparser": "1.5.0" - }, - "dependencies": { - "ansi-regex": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", - "dev": true - }, - "camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", - "dev": true - }, - "get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "dev": true - }, - "require-main-filename": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", - "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", - "dev": true - }, - "string-width": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", - "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", - "dev": true, - "requires": { - "emoji-regex": "^7.0.1", - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^5.1.0" - } - }, - "strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", - "dev": true, - "requires": { - "ansi-regex": "^4.1.0" - } - }, - "which": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", - "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", - "dev": true, - "requires": { - "isexe": "^2.0.0" - } - }, - "yargs": { - "version": "13.2.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.2.2.tgz", - "integrity": "sha512-WyEoxgyTD3w5XRpAQNYUB9ycVH/PQrToaTXdYXRdOXvEy1l19br+VJsc0vcO8PTGg5ro/l/GY7F/JMEBmI0BxA==", - "dev": true, - "requires": { - "cliui": "^4.0.0", - "find-up": "^3.0.0", - "get-caller-file": "^2.0.1", - "os-locale": "^3.1.0", - "require-directory": "^2.1.1", - "require-main-filename": "^2.0.0", - "set-blocking": "^2.0.0", - "string-width": "^3.0.0", - "which-module": "^2.0.0", - "y18n": "^4.0.0", - "yargs-parser": "^13.0.0" - } - }, - "yargs-parser": { - "version": "13.0.0", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.0.0.tgz", - "integrity": "sha512-w2LXjoL8oRdRQN+hOyppuXs+V/fVAYtpcrRxZuF7Kt/Oc+Jr2uAcVntaUTNT6w5ihoWfFDpNY8CPx1QskxZ/pw==", - "dev": true, - "requires": { - "camelcase": "^5.0.0", - "decamelize": "^1.2.0" - } + "node_modules/node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "license": "MIT", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true } } }, - "move-concurrently": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/move-concurrently/-/move-concurrently-1.0.1.tgz", - "integrity": "sha1-viwAX9oy4LKa8fBdfEszIUxwH5I=", + "node_modules/npm-run-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-6.0.0.tgz", + "integrity": "sha512-9qny7Z9DsQU8Ou39ERsPU4OZQlSTP47ShQzuKZ6PRXpYLtIFgl/DEBYEXKlvcEa+9tHVcK8CF81Y2V72qaZhWA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^4.0.0", + "unicorn-magic": "^0.3.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/npm-run-path/node_modules/path-key": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", + "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", "dev": true, - "requires": { - "aproba": "^1.1.1", - "copy-concurrently": "^1.0.0", - "fs-write-stream-atomic": "^1.0.8", - "mkdirp": "^0.5.1", - "rimraf": "^2.5.4", - "run-queue": "^1.0.3" + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "ms": { + "node_modules/nth-check": { "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", - "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", - "dev": true - }, - "nan": { - "version": "2.14.0", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.0.tgz", - "integrity": "sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg==", - "dev": true, - "optional": true - }, - "nanomatch": { - "version": "1.2.13", - "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz", - "integrity": "sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==", - "dev": true, - "requires": { - "arr-diff": "^4.0.0", - "array-unique": "^0.3.2", - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "fragment-cache": "^0.2.1", - "is-windows": "^1.0.2", - "kind-of": "^6.0.2", - "object.pick": "^1.3.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - } - }, - "neo-async": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.1.tgz", - "integrity": "sha512-iyam8fBuCUpWeKPGpaNMetEocMt364qkCsfL9JuhjXX6dRnguRVOfk2GZaDpPjcOKiiXCPINZC1GczQ7iTq3Zw==", - "dev": true - }, - "nice-try": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", - "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", - "dev": true - }, - "node-atomizr": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/node-atomizr/-/node-atomizr-0.6.1.tgz", - "integrity": "sha512-kgUgQSMUdP3G44MNB9avNyo3uSDgmvSolZKU6+ZOidIpwhO2NSpEcV7/6rHNj8965/6ONX1cVS+YWcX7zI8sGA==", - "dev": true, - "requires": { - "commander": "^2.9.0", - "cson-parser": "^2.0.0", - "glob": "^7.1.1", - "parse-json": "^3.0.0", - "plist": "^2.0.1", - "update-notifier": "^2.1.0", - "uuid": "^3.0.1", - "wildglob": "^0.1.1", - "xml-js": "^1.0.2" - }, - "dependencies": { - "chalk": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.1.tgz", - "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "glob": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", - "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", - "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", - "dev": true - }, - "inherits": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", - "dev": true - }, - "minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "dev": true, - "requires": { - "brace-expansion": "^1.1.7" - } - }, - "supports-color": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.4.0.tgz", - "integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - }, - "update-notifier": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/update-notifier/-/update-notifier-2.5.0.tgz", - "integrity": "sha512-gwMdhgJHGuj/+wHJJs9e6PcCszpxR1b236igrOkUofGhqJuG+amlIKwApH1IW1WWl7ovZxsX49lMBWLxSdm5Dw==", - "dev": true, - "requires": { - "boxen": "^1.2.1", - "chalk": "^2.0.1", - "configstore": "^3.0.0", - "import-lazy": "^2.1.0", - "is-ci": "^1.0.10", - "is-installed-globally": "^0.1.0", - "is-npm": "^1.0.0", - "latest-version": "^3.0.0", - "semver-diff": "^2.0.0", - "xdg-basedir": "^3.0.0" - } - }, - "uuid": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.2.1.tgz", - "integrity": "sha512-jZnMwlb9Iku/O3smGWvZhauCf6cvvpKi4BKRiliS3cxnI+Gz9j5MEpTz2UFuXiKPJocb7gnsLHwiS05ige5BEA==", - "dev": true - } + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", + "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", + "license": "BSD-2-Clause", + "dependencies": { + "boolbase": "^1.0.0" + }, + "funding": { + "url": "https://github.com/fb55/nth-check?sponsor=1" } }, - "node-environment-flags": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/node-environment-flags/-/node-environment-flags-1.0.5.tgz", - "integrity": "sha512-VNYPRfGfmZLx0Ye20jWzHUjyTW/c+6Wq+iLhDzUI4XmhrDd9l/FozXV3F2xOaXjvp0co0+v1YSR3CMP6g+VvLQ==", + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", "dev": true, - "requires": { - "object.getownpropertydescriptors": "^2.0.3", - "semver": "^5.7.0" - }, + "license": "ISC", "dependencies": { - "semver": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.0.tgz", - "integrity": "sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA==", - "dev": true - } + "wrappy": "1" } }, - "node-libs-browser": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/node-libs-browser/-/node-libs-browser-2.2.1.tgz", - "integrity": "sha512-h/zcD8H9kaDZ9ALUWwlBUDo6TKF8a7qBSCSEGfjTVIYeqsioSKaAX+BN7NgiMGp6iSIXZ3PxgCu8KS3b71YK5Q==", - "dev": true, - "requires": { - "assert": "^1.1.1", - "browserify-zlib": "^0.2.0", - "buffer": "^4.3.0", - "console-browserify": "^1.1.0", - "constants-browserify": "^1.0.0", - "crypto-browserify": "^3.11.0", - "domain-browser": "^1.1.1", - "events": "^3.0.0", - "https-browserify": "^1.0.0", - "os-browserify": "^0.3.0", - "path-browserify": "0.0.1", - "process": "^0.11.10", - "punycode": "^1.2.4", - "querystring-es3": "^0.2.0", - "readable-stream": "^2.3.3", - "stream-browserify": "^2.0.1", - "stream-http": "^2.7.2", - "string_decoder": "^1.0.0", - "timers-browserify": "^2.0.4", - "tty-browserify": "0.0.0", - "url": "^0.11.0", - "util": "^0.11.0", - "vm-browserify": "^1.0.1" - }, - "dependencies": { - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", - "dev": true - }, - "punycode": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", - "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=", - "dev": true - }, - "readable-stream": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", - "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", - "dev": true, - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - }, - "dependencies": { - "string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dev": true, - "requires": { - "safe-buffer": "~5.1.0" - } - } - } - }, - "string_decoder": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "dev": true, - "requires": { - "safe-buffer": "~5.2.0" - }, - "dependencies": { - "safe-buffer": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.0.tgz", - "integrity": "sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg==", - "dev": true - } - } - }, - "util": { - "version": "0.11.1", - "resolved": "https://registry.npmjs.org/util/-/util-0.11.1.tgz", - "integrity": "sha512-HShAsny+zS2TZfaXxD9tYj4HQGlBezXZMZuM/S5PKLLoZkShZiGk9o5CzukI1LVHZvjdvZ2Sj1aW/Ndn2NB/HQ==", - "dev": true, - "requires": { - "inherits": "2.0.3" - } - } + "node_modules/onetime": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-7.0.0.tgz", + "integrity": "sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "mimic-function": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "dev": true - }, - "npm-run-path": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", - "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=", + "node_modules/optionator": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", "dev": true, - "requires": { - "path-key": "^2.0.0" + "license": "MIT", + "dependencies": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.5" + }, + "engines": { + "node": ">= 0.8.0" } }, - "number-is-nan": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", - "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", - "dev": true - }, - "oauth-sign": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", - "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==", - "dev": true - }, - "object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", - "dev": true - }, - "object-copy": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz", - "integrity": "sha1-fn2Fi3gb18mRpBupde04EnVOmYw=", - "dev": true, - "requires": { - "copy-descriptor": "^0.1.0", - "define-property": "^0.2.5", - "kind-of": "^3.0.3" - }, - "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, - "requires": { - "is-descriptor": "^0.1.0" - } - }, - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } + "node_modules/ora": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/ora/-/ora-8.2.0.tgz", + "integrity": "sha512-weP+BZ8MVNnlCm8c0Qdc1WSWq4Qn7I+9CJGm7Qali6g44e/PUzbjNqJX5NJ9ljlNMosfJvg1fKEGILklK9cwnw==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^5.3.0", + "cli-cursor": "^5.0.0", + "cli-spinners": "^2.9.2", + "is-interactive": "^2.0.0", + "is-unicode-supported": "^2.0.0", + "log-symbols": "^6.0.0", + "stdin-discarder": "^0.2.2", + "string-width": "^7.2.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "object-keys": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", - "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", - "dev": true - }, - "object-visit": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz", - "integrity": "sha1-95xEk68MU3e1n+OdOV5BBC3QRbs=", + "node_modules/ora/node_modules/ansi-regex": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", + "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", "dev": true, - "requires": { - "isobject": "^3.0.0" + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" } }, - "object.assign": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.0.tgz", - "integrity": "sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w==", + "node_modules/ora/node_modules/chalk": { + "version": "5.6.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.2.tgz", + "integrity": "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==", "dev": true, - "requires": { - "define-properties": "^1.1.2", - "function-bind": "^1.1.1", - "has-symbols": "^1.0.0", - "object-keys": "^1.0.11" + "license": "MIT", + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "object.getownpropertydescriptors": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.0.3.tgz", - "integrity": "sha1-h1jIRvW0B62rDyNuCYbxSwUcqhY=", + "node_modules/ora/node_modules/emoji-regex": { + "version": "10.6.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.6.0.tgz", + "integrity": "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==", + "dev": true, + "license": "MIT" + }, + "node_modules/ora/node_modules/log-symbols": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-6.0.0.tgz", + "integrity": "sha512-i24m8rpwhmPIS4zscNzK6MSEhk0DUWa/8iYQWxhffV8jkI4Phvs3F+quL5xvS0gdQR0FyTCMMH33Y78dDTzzIw==", "dev": true, - "requires": { - "define-properties": "^1.1.2", - "es-abstract": "^1.5.1" + "license": "MIT", + "dependencies": { + "chalk": "^5.3.0", + "is-unicode-supported": "^1.3.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "object.pick": { + "node_modules/ora/node_modules/log-symbols/node_modules/is-unicode-supported": { "version": "1.3.0", - "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz", - "integrity": "sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c=", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-1.3.0.tgz", + "integrity": "sha512-43r2mRvz+8JRIKnWJ+3j8JtjRKZ6GmjzfaE/qiBJnikNnYv/6bagRJ1kUhNk8R5EX/GkobD+r+sfxCPJsiKBLQ==", "dev": true, - "requires": { - "isobject": "^3.0.1" + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "node_modules/ora/node_modules/string-width": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", + "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", "dev": true, - "requires": { - "wrappy": "1" + "license": "MIT", + "dependencies": { + "emoji-regex": "^10.3.0", + "get-east-asian-width": "^1.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "ondone": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/ondone/-/ondone-1.0.0.tgz", - "integrity": "sha1-+yCDFtesKVG9zGlqfYZL8hr3oAg=", - "dev": true - }, - "os-browserify": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/os-browserify/-/os-browserify-0.3.0.tgz", - "integrity": "sha1-hUNzx/XCMVkU/Jv8a9gjj92h7Cc=", - "dev": true - }, - "os-locale": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-3.1.0.tgz", - "integrity": "sha512-Z8l3R4wYWM40/52Z+S265okfFj8Kt2cC2MKY+xNi3kFs+XGI7WXu/I309QQQYbRW4ijiZ+yxs9pqEhJh0DqW3Q==", - "dev": true, - "requires": { - "execa": "^1.0.0", - "lcid": "^2.0.0", - "mem": "^4.0.0" - }, - "dependencies": { - "cross-spawn": { - "version": "6.0.5", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", - "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", - "dev": true, - "requires": { - "nice-try": "^1.0.4", - "path-key": "^2.0.1", - "semver": "^5.5.0", - "shebang-command": "^1.2.0", - "which": "^1.2.9" - } - }, - "execa": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz", - "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==", - "dev": true, - "requires": { - "cross-spawn": "^6.0.0", - "get-stream": "^4.0.0", - "is-stream": "^1.1.0", - "npm-run-path": "^2.0.0", - "p-finally": "^1.0.0", - "signal-exit": "^3.0.0", - "strip-eof": "^1.0.0" - } - }, - "get-stream": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", - "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", - "dev": true, - "requires": { - "pump": "^3.0.0" - } - } + "node_modules/ora/node_modules/strip-ansi": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz", + "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.2.2" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" } }, - "p-defer": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-defer/-/p-defer-1.0.0.tgz", - "integrity": "sha1-n26xgvbJqozXQwBKfU+WsZaw+ww=", - "dev": true - }, - "p-finally": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", - "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=", - "dev": true - }, - "p-is-promise": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/p-is-promise/-/p-is-promise-2.0.0.tgz", - "integrity": "sha512-pzQPhYMCAgLAKPWD2jC3Se9fEfrD9npNos0y150EeqZll7akhEgGhTW/slB6lHku8AvYGiJ+YJ5hfHKePPgFWg==", - "dev": true - }, - "p-limit": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.0.tgz", - "integrity": "sha512-pZbTJpoUsCzV48Mc9Nh51VbwO0X9cuPFE8gYwx9BTCt9SF8/b7Zljd2fVgOxhIF/HDTKgpVzs+GPhyKfjLLFRQ==", + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", "dev": true, - "requires": { - "p-try": "^2.0.0" + "license": "MIT", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "p-locate": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", - "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", "dev": true, - "requires": { - "p-limit": "^2.0.0" + "license": "MIT", + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", - "dev": true - }, - "package-json": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/package-json/-/package-json-4.0.1.tgz", - "integrity": "sha1-iGmgQBJTZhxMTKPabCEh7VVfXu0=", + "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, - "requires": { - "got": "^6.7.1", - "registry-auth-token": "^3.0.1", - "registry-url": "^3.0.3", - "semver": "^5.1.0" - } + "license": "BlueOak-1.0.0" }, - "pako": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.10.tgz", - "integrity": "sha512-0DTvPVU3ed8+HNXOu5Bs+o//Mbdj9VNQMUOe9oKCwh8l0GNwpTDMKCWbRjgtD291AWnkAgkqA/LOnQS8AmS1tw==", - "dev": true + "node_modules/pako": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", + "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==", + "dev": true, + "license": "(MIT AND Zlib)" }, - "parallel-transform": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/parallel-transform/-/parallel-transform-1.2.0.tgz", - "integrity": "sha512-P2vSmIu38uIlvdcU7fDkyrxj33gTUy/ABO5ZUbGowxNCopBq/OoD42bP4UmMrJoPyk4Uqf0mu3mtWBhHCZD8yg==", + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", "dev": true, - "requires": { - "cyclist": "^1.0.1", - "inherits": "^2.0.3", - "readable-stream": "^2.1.5" - }, + "license": "MIT", "dependencies": { - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", - "dev": true - }, - "readable-stream": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", - "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", - "dev": true, - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dev": true, - "requires": { - "safe-buffer": "~5.1.0" - } - } + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" } }, - "parse-asn1": { - "version": "5.1.5", - "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.5.tgz", - "integrity": "sha512-jkMYn1dcJqF6d5CpU689bq7w/b5ALS9ROVSpQDPrZsqqesUJii9qutvoT5ltGedNXMO2e16YUWIghG9KxaViTQ==", + "node_modules/parse-ms": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse-ms/-/parse-ms-4.0.0.tgz", + "integrity": "sha512-TXfryirbmq34y8QBwgqCVLi+8oA3oWx2eAnSn62ITyEhEYaWRlVZ2DvMM9eZbMs/RfxPu/PK/aBLyGj4IrqMHw==", "dev": true, - "requires": { - "asn1.js": "^4.0.0", - "browserify-aes": "^1.0.0", - "create-hash": "^1.1.0", - "evp_bytestokey": "^1.0.0", - "pbkdf2": "^3.0.3", - "safe-buffer": "^5.1.1" + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "parse-json": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-3.0.0.tgz", - "integrity": "sha1-+m9HsY4jgm6tMvJj50TQ4ehH+xM=", - "dev": true, - "requires": { - "error-ex": "^1.3.1" + "node_modules/parse5": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.3.0.tgz", + "integrity": "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==", + "license": "MIT", + "dependencies": { + "entities": "^6.0.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" } }, - "parse-passwd": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/parse-passwd/-/parse-passwd-1.0.0.tgz", - "integrity": "sha1-bVuTSkVpk7I9N/QKOC1vFmao5cY=", - "dev": true - }, - "pascalcase": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz", - "integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=", - "dev": true - }, - "path": { - "version": "0.12.7", - "resolved": "https://registry.npmjs.org/path/-/path-0.12.7.tgz", - "integrity": "sha1-1NwqUGxM4hl+tIHr/NWzbAFAsQ8=", - "requires": { - "process": "^0.11.1", - "util": "^0.10.3" - } - }, - "path-browserify": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-0.0.1.tgz", - "integrity": "sha512-BapA40NHICOS+USX9SN4tyhq+A2RrN/Ws5F0Z5aMHDp98Fl86lX8Oti8B7uN93L4Ifv4fHOEA+pQw87gmMO/lQ==", - "dev": true + "node_modules/parse5-htmlparser2-tree-adapter": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-7.1.0.tgz", + "integrity": "sha512-ruw5xyKs6lrpo9x9rCZqZZnIUntICjQAd0Wsmp396Ul9lN/h+ifgVV1x1gZHi8euej6wTfpqX8j+BFQxF0NS/g==", + "license": "MIT", + "dependencies": { + "domhandler": "^5.0.3", + "parse5": "^7.0.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } }, - "path-dirname": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/path-dirname/-/path-dirname-1.0.2.tgz", - "integrity": "sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA=", - "dev": true + "node_modules/parse5/node_modules/entities": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-6.0.1.tgz", + "integrity": "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } }, - "path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", - "dev": true + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } }, - "path-is-absolute": { + "node_modules/path-is-absolute": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", - "dev": true - }, - "path-is-inside": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz", - "integrity": "sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM=", - "dev": true - }, - "path-key": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", - "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=", - "dev": true + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } }, - "path-parse": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", - "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==", - "dev": true + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } }, - "pbkdf2": { - "version": "3.0.17", - "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.0.17.tgz", - "integrity": "sha512-U/il5MsrZp7mGg3mSQfn742na2T+1/vHDCG5/iTI3X9MKUuYUZVLQhyRsg06mCgDBTd57TxzgZt7P+fYfjRLtA==", + "node_modules/path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", "dev": true, - "requires": { - "create-hash": "^1.1.2", - "create-hmac": "^1.1.4", - "ripemd160": "^2.0.1", - "safe-buffer": "^5.0.1", - "sha.js": "^2.4.8" + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "performance-now": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", - "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=", - "dev": true + "node_modules/path-to-regexp": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-6.3.0.tgz", + "integrity": "sha512-Yhpw4T9C6hPpgPeA28us07OJeqZ5EzQTkbfwuhsUg0c237RomFoETJgmp2sa3F/41gfLE6G5cqcYwznmeEeOlQ==", + "dev": true, + "license": "MIT" }, - "picomatch": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.0.7.tgz", - "integrity": "sha512-oLHIdio3tZ0qH76NybpeneBhYVj0QFTfXEFTc/B3zKQspYfYYkWYgFsmzo+4kvId/bQRcNkVeguI3y+CD22BtA==", - "dev": true + "node_modules/path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } }, - "pify": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", - "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", - "dev": true + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } }, - "pkg-dir": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-3.0.0.tgz", - "integrity": "sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==", + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", "dev": true, - "requires": { - "find-up": "^3.0.0" + "license": "MIT", + "engines": { + "node": ">= 0.8.0" } }, - "plist": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/plist/-/plist-2.1.0.tgz", - "integrity": "sha1-V8zbeggh3yGDEhejytVOPhRqECU=", + "node_modules/pretty-ms": { + "version": "9.3.0", + "resolved": "https://registry.npmjs.org/pretty-ms/-/pretty-ms-9.3.0.tgz", + "integrity": "sha512-gjVS5hOP+M3wMm5nmNOucbIrqudzs9v/57bWRHQWLYklXqoXKrVfYW2W9+glfGsqtPgpiz5WwyEEB+ksXIx3gQ==", "dev": true, - "requires": { - "base64-js": "1.2.0", - "xmlbuilder": "8.2.2", - "xmldom": "0.1.x" + "license": "MIT", + "dependencies": { + "parse-ms": "^4.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "posix-character-classes": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz", - "integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=", - "dev": true - }, - "prepend-http": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-1.0.4.tgz", - "integrity": "sha1-1PRWKwzjaW5BrFLQ4ALlemNdxtw=", - "dev": true - }, - "process": { - "version": "0.11.10", - "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", - "integrity": "sha1-czIwDoQBYb2j5podHZGn1LwW8YI=" - }, - "process-nextick-args": { + "node_modules/process-nextick-args": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", - "dev": true - }, - "promise-inflight": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz", - "integrity": "sha1-mEcocL8igTL8vdhoEputEsPAKeM=", - "dev": true - }, - "prr": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz", - "integrity": "sha1-0/wRS6BplaRexok/SEzrHXj19HY=", - "dev": true - }, - "pseudomap": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", - "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=", - "dev": true - }, - "psl": { - "version": "1.1.31", - "resolved": "https://registry.npmjs.org/psl/-/psl-1.1.31.tgz", - "integrity": "sha512-/6pt4+C+T+wZUieKR620OpzN/LlnNKuWjy1iFLQ/UG35JqHlR/89MP1d96dUfkf6Dne3TuLQzOYEYshJ+Hx8mw==", - "dev": true + "dev": true, + "license": "MIT" }, - "public-encrypt": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.3.tgz", - "integrity": "sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q==", + "node_modules/progress": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", + "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", "dev": true, - "requires": { - "bn.js": "^4.1.0", - "browserify-rsa": "^4.0.0", - "create-hash": "^1.1.0", - "parse-asn1": "^5.0.0", - "randombytes": "^2.0.1", - "safe-buffer": "^5.1.2" + "license": "MIT", + "engines": { + "node": ">=0.4.0" } }, - "pump": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", - "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", - "dev": true, - "requires": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" - } - }, - "pumpify": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/pumpify/-/pumpify-1.5.1.tgz", - "integrity": "sha512-oClZI37HvuUJJxSKKrC17bZ9Cu0ZYhEAGPsPUy9KlMUmv9dKX2o77RUmq7f3XjIxbwyGwYzbzQ1L2Ks8sIradQ==", - "dev": true, - "requires": { - "duplexify": "^3.6.0", - "inherits": "^2.0.3", - "pump": "^2.0.0" - }, - "dependencies": { - "pump": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/pump/-/pump-2.0.1.tgz", - "integrity": "sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA==", - "dev": true, - "requires": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" - } - } + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" } }, - "punycode": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", - "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", - "dev": true - }, - "qs": { - "version": "6.5.2", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", - "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==", - "dev": true - }, - "querystring": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", - "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=", - "dev": true - }, - "querystring-es3": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/querystring-es3/-/querystring-es3-0.2.1.tgz", - "integrity": "sha1-nsYfeQSYdXB9aUFFlv2Qek1xHnM=", - "dev": true - }, - "querystringify": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.1.1.tgz", - "integrity": "sha512-w7fLxIRCRT7U8Qu53jQnJyPkYZIaR4n5151KMfcJlO/A9397Wxb1amJvROTK6TOnp7PfoAmg/qXiNHI+08jRfA==", - "dev": true + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "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" + } + ], + "license": "MIT" }, - "randombytes": { + "node_modules/randombytes": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", "dev": true, - "requires": { + "license": "MIT", + "dependencies": { "safe-buffer": "^5.1.0" } }, - "randomfill": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/randomfill/-/randomfill-1.0.4.tgz", - "integrity": "sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw==", + "node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", "dev": true, - "requires": { - "randombytes": "^2.0.5", - "safe-buffer": "^5.1.0" + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" } }, - "rc": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.7.tgz", - "integrity": "sha512-LdLD8xD4zzLsAT5xyushXDNscEjB7+2ulnl8+r1pnESlYtlJtVSoCMBGr30eDRJ3+2Gq89jK9P9e4tCEH1+ywA==", + "node_modules/readdirp": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", + "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", "dev": true, - "requires": { - "deep-extend": "^0.5.1", - "ini": "~1.3.0", - "minimist": "^1.2.0", - "strip-json-comments": "~2.0.1" + "license": "MIT", + "engines": { + "node": ">= 14.18.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" } }, - "readable-stream": { - "version": "1.0.34", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", - "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=", + "node_modules/regexpp": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", + "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", "dev": true, - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.1", - "isarray": "0.0.1", - "string_decoder": "~0.10.x" + "license": "MIT", + "engines": { + "node": ">=8" }, - "dependencies": { - "inherits": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", - "dev": true - } - } - }, - "readdirp": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.2.1.tgz", - "integrity": "sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ==", - "dev": true, - "requires": { - "graceful-fs": "^4.1.11", - "micromatch": "^3.1.10", - "readable-stream": "^2.0.2" - }, - "dependencies": { - "braces": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", - "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", - "dev": true, - "requires": { - "arr-flatten": "^1.1.0", - "array-unique": "^0.3.2", - "extend-shallow": "^2.0.1", - "fill-range": "^4.0.0", - "isobject": "^3.0.1", - "repeat-element": "^1.1.2", - "snapdragon": "^0.8.1", - "snapdragon-node": "^2.0.1", - "split-string": "^3.0.2", - "to-regex": "^3.0.1" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "fill-range": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", - "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", - "dev": true, - "requires": { - "extend-shallow": "^2.0.1", - "is-number": "^3.0.0", - "repeat-string": "^1.6.1", - "to-regex-range": "^2.1.0" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "is-number": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", - "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", - "dev": true - }, - "micromatch": { - "version": "3.1.10", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", - "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", - "dev": true, - "requires": { - "arr-diff": "^4.0.0", - "array-unique": "^0.3.2", - "braces": "^2.3.1", - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "extglob": "^2.0.4", - "fragment-cache": "^0.2.1", - "kind-of": "^6.0.2", - "nanomatch": "^1.2.9", - "object.pick": "^1.3.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.2" - } - }, - "readable-stream": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", - "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", - "dev": true, - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dev": true, - "requires": { - "safe-buffer": "~5.1.0" - } - }, - "to-regex-range": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", - "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", - "dev": true, - "requires": { - "is-number": "^3.0.0", - "repeat-string": "^1.6.1" - } - } + "funding": { + "url": "https://github.com/sponsors/mysticatea" } }, - "regex-not": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz", - "integrity": "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==", + "node_modules/regextras": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/regextras/-/regextras-0.8.0.tgz", + "integrity": "sha512-k519uI04Z3SaY0fLX843MRXnDeG2+vHOFsyhiPZvNLe7r8rD2YNRjq4BQLZZ0oAr2NrtvZlICsXysGNFPGa3CQ==", "dev": true, - "requires": { - "extend-shallow": "^3.0.2", - "safe-regex": "^1.1.0" + "license": "MIT", + "engines": { + "node": ">=0.1.14" } }, - "registry-auth-token": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-3.3.2.tgz", - "integrity": "sha512-JL39c60XlzCVgNrO+qq68FoNb56w/m7JYvGR2jT5iR1xBrUA3Mfx5Twk5rqTThPmQKMWydGmq8oFtDlxfrmxnQ==", + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", "dev": true, - "requires": { - "rc": "^1.1.6", - "safe-buffer": "^5.0.1" + "license": "MIT", + "engines": { + "node": ">=0.10.0" } }, - "registry-url": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/registry-url/-/registry-url-3.1.0.tgz", - "integrity": "sha1-PU74cPc93h138M+aOBQyRE4XSUI=", + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", "dev": true, - "requires": { - "rc": "^1.0.1" + "license": "MIT", + "engines": { + "node": ">=0.10.0" } }, - "remove-trailing-separator": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", - "integrity": "sha1-wkvOKig62tW8P1jg1IJJuSN52O8=", - "dev": true - }, - "repeat-element": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.3.tgz", - "integrity": "sha512-ahGq0ZnV5m5XtZLMb+vP76kcAM5nkLqk0lpqAuojSKGgQtn4eRi4ZZGm2olo2zKFH+sMsWaqOCW1dqAnOru72g==", - "dev": true - }, - "repeat-string": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", - "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=", - "dev": true - }, - "request": { - "version": "2.88.0", - "resolved": "https://registry.npmjs.org/request/-/request-2.88.0.tgz", - "integrity": "sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg==", - "dev": true, - "requires": { - "aws-sign2": "~0.7.0", - "aws4": "^1.8.0", - "caseless": "~0.12.0", - "combined-stream": "~1.0.6", - "extend": "~3.0.2", - "forever-agent": "~0.6.1", - "form-data": "~2.3.2", - "har-validator": "~5.1.0", - "http-signature": "~1.2.0", - "is-typedarray": "~1.0.0", - "isstream": "~0.1.2", - "json-stringify-safe": "~5.0.1", - "mime-types": "~2.1.19", - "oauth-sign": "~0.9.0", - "performance-now": "^2.1.0", - "qs": "~6.5.2", - "safe-buffer": "^5.1.2", - "tough-cookie": "~2.4.3", - "tunnel-agent": "^0.6.0", - "uuid": "^3.3.2" - } - }, - "require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", - "dev": true - }, - "require-main-filename": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz", - "integrity": "sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=", - "dev": true - }, - "requires-port": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", - "integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=", - "dev": true - }, - "resolve": { - "version": "1.12.0", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.12.0.tgz", - "integrity": "sha512-B/dOmuoAik5bKcD6s6nXDCjzUKnaDvdkRyAk6rsmsKLipWj4797iothd7jmmUhWTfinVMU+wc56rYKsit2Qy4w==", + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", "dev": true, - "requires": { - "path-parse": "^1.0.6" + "license": "MIT", + "engines": { + "node": ">=4" } }, - "resolve-cwd": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-2.0.0.tgz", - "integrity": "sha1-AKn3OHVW4nA46uIyyqNypqWbZlo=", + "node_modules/restore-cursor": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-5.1.0.tgz", + "integrity": "sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==", "dev": true, - "requires": { - "resolve-from": "^3.0.0" + "license": "MIT", + "dependencies": { + "onetime": "^7.0.0", + "signal-exit": "^4.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "resolve-dir": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/resolve-dir/-/resolve-dir-1.0.1.tgz", - "integrity": "sha1-eaQGRMNivoLybv/nOcm7U4IEb0M=", - "dev": true, - "requires": { - "expand-tilde": "^2.0.0", - "global-modules": "^1.0.0" - }, - "dependencies": { - "global-modules": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-1.0.0.tgz", - "integrity": "sha512-sKzpEkf11GpOFuw0Zzjzmt4B4UZwjOcG757PPvrfhxcLFbq0wpsgpOqxpxtxFiCG4DtG93M6XRVbF2oGdev7bg==", - "dev": true, - "requires": { - "global-prefix": "^1.0.1", - "is-windows": "^1.0.1", - "resolve-dir": "^1.0.0" - } - } + "node_modules/reusify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", + "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", + "dev": true, + "license": "MIT", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" } }, - "resolve-from": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz", - "integrity": "sha1-six699nWiBvItuZTM17rywoYh0g=", - "dev": true - }, - "resolve-url": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz", - "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=", - "dev": true - }, - "ret": { - "version": "0.1.15", - "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz", - "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==", - "dev": true - }, - "rimraf": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", - "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", - "dev": true, - "requires": { + "node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "deprecated": "Rimraf versions prior to v4 are no longer supported", + "dev": true, + "license": "ISC", + "dependencies": { "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "ripemd160": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz", - "integrity": "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==", + "node_modules/rimraf/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", "dev": true, - "requires": { - "hash-base": "^3.0.0", - "inherits": "^2.0.1" + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "run-queue": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/run-queue/-/run-queue-1.0.3.tgz", - "integrity": "sha1-6Eg5bwV9Ij8kOGkkYY4laUFh7Ec=", + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", "dev": true, - "requires": { - "aproba": "^1.1.1" + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "queue-microtask": "^1.2.2" } }, - "safe-buffer": { + "node_modules/safe-buffer": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true - }, - "safe-regex": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", - "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=", "dev": true, - "requires": { - "ret": "~0.1.10" - } - }, - "safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", - "dev": true + "license": "MIT" }, - "sax": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", - "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==", - "dev": true - }, - "schema-utils": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz", - "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==", - "dev": true, - "requires": { - "ajv": "^6.1.0", - "ajv-errors": "^1.0.0", - "ajv-keywords": "^3.1.0" + "node_modules/semver": { + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", + "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" } }, - "semver": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.5.0.tgz", - "integrity": "sha512-4SJ3dm0WAwWy/NVeioZh5AntkdJoWKxHxcmyP622fOkgHa4z3R0TdBJICINyaSDE6uNwVc8gZr+ZinwZAH4xIA==", - "dev": true - }, - "semver-diff": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/semver-diff/-/semver-diff-2.1.0.tgz", - "integrity": "sha1-S7uEN8jTfksM8aaP1ybsbWRdbTY=", + "node_modules/serialize-javascript": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz", + "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==", "dev": true, - "requires": { - "semver": "^5.0.3" - } - }, - "serialize-javascript": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-1.9.1.tgz", - "integrity": "sha512-0Vb/54WJ6k5v8sSWN09S0ora+Hnr+cX40r9F170nT+mSkaxltoE/7R3OrIdBSUv1OoiobH1QoWQbCnAO+e8J1A==", - "dev": true - }, - "set-blocking": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", - "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", - "dev": true - }, - "set-value": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz", - "integrity": "sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==", - "dev": true, - "requires": { - "extend-shallow": "^2.0.1", - "is-extendable": "^0.1.1", - "is-plain-object": "^2.0.3", - "split-string": "^3.0.1" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } + "license": "BSD-3-Clause", + "dependencies": { + "randombytes": "^2.1.0" } }, - "setimmediate": { + "node_modules/setimmediate": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", - "integrity": "sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU=", - "dev": true + "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==", + "dev": true, + "license": "MIT" }, - "sha.js": { - "version": "2.4.11", - "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", - "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==", + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", "dev": true, - "requires": { - "inherits": "^2.0.1", - "safe-buffer": "^5.0.1" + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" } }, - "shebang-command": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", - "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", "dev": true, - "requires": { - "shebang-regex": "^1.0.0" + "license": "MIT", + "engines": { + "node": ">=8" } }, - "shebang-regex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", - "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", - "dev": true + "node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } }, - "signal-exit": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", - "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", - "dev": true - }, - "snapdragon": { - "version": "0.8.2", - "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz", - "integrity": "sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==", - "dev": true, - "requires": { - "base": "^0.11.1", - "debug": "^2.2.0", - "define-property": "^0.2.5", - "extend-shallow": "^2.0.1", - "map-cache": "^0.2.2", - "source-map": "^0.5.6", - "source-map-resolve": "^0.5.0", - "use": "^3.1.0" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - }, - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, - "requires": { - "is-descriptor": "^0.1.0" - } - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true - } + "node_modules/sinon": { + "version": "15.2.0", + "resolved": "https://registry.npmjs.org/sinon/-/sinon-15.2.0.tgz", + "integrity": "sha512-nPS85arNqwBXaIsFCkolHjGIkFo+Oxu9vbgmBJizLAhqe6P2o3Qmj3KCUoRkfhHtvgDhZdWD3risLHAUJ8npjw==", + "deprecated": "16.1.1", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@sinonjs/commons": "^3.0.0", + "@sinonjs/fake-timers": "^10.3.0", + "@sinonjs/samsam": "^8.0.0", + "diff": "^5.1.0", + "nise": "^5.1.4", + "supports-color": "^7.2.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/sinon" } }, - "snapdragon-node": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz", - "integrity": "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==", - "dev": true, - "requires": { - "define-property": "^1.0.0", - "isobject": "^3.0.0", - "snapdragon-util": "^3.0.1" - }, - "dependencies": { - "define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", - "dev": true, - "requires": { - "is-descriptor": "^1.0.0" - } - }, - "is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - } - } + "node_modules/sinon/node_modules/diff": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-5.2.2.tgz", + "integrity": "sha512-vtcDfH3TOjP8UekytvnHH1o1P4FcUdt4eQ1Y+Abap1tk/OB2MWQvcwS2ClCd1zuIhc3JKOx6p3kod8Vfys3E+A==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.3.1" } }, - "snapdragon-util": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz", - "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==", + "node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", "dev": true, - "requires": { - "kind-of": "^3.2.0" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } + "license": "MIT", + "engines": { + "node": ">=8" } }, - "source-list-map": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/source-list-map/-/source-list-map-2.0.1.tgz", - "integrity": "sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw==", - "dev": true - }, - "source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", - "dev": true - }, - "source-map-resolve": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.2.tgz", - "integrity": "sha512-MjqsvNwyz1s0k81Goz/9vRBe9SZdB09Bdw+/zYyO+3CuPk6fouTaxscHkgtE8jKvf01kVfl8riHzERQ/kefaSA==", - "dev": true, - "requires": { - "atob": "^2.1.1", - "decode-uri-component": "^0.2.0", - "resolve-url": "^0.2.1", - "source-map-url": "^0.4.0", - "urix": "^0.1.0" - } - }, - "source-map-support": { - "version": "0.5.11", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.11.tgz", - "integrity": "sha512-//sajEx/fGL3iw6fltKMdPvy8kL3kJ2O3iuYlRoT3k9Kb4BjOoZ+BZzaNHeuaruSt+Kf3Zk9tnfAQg9/AJqUVQ==", - "dev": true, - "requires": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" - }, - "dependencies": { - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - } + "node_modules/slice-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", + "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "astral-regex": "^2.0.0", + "is-fullwidth-code-point": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/slice-ansi?sponsor=1" } }, - "source-map-url": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.0.tgz", - "integrity": "sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM=", - "dev": true + "node_modules/spdx-exceptions": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.5.0.tgz", + "integrity": "sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==", + "dev": true, + "license": "CC-BY-3.0" }, - "split-string": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz", - "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==", + "node_modules/spdx-expression-parse": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", + "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", "dev": true, - "requires": { - "extend-shallow": "^3.0.0" + "license": "MIT", + "dependencies": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" } }, - "sprintf-js": { + "node_modules/spdx-license-ids": { + "version": "3.0.23", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.23.tgz", + "integrity": "sha512-CWLcCCH7VLu13TgOH+r8p1O/Znwhqv/dbb6lqWy67G+pT1kHmeD/+V36AVb/vq8QMIQwVShJ6Ssl5FPh0fuSdw==", + "dev": true, + "license": "CC0-1.0" + }, + "node_modules/sprintf-js": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", - "dev": true - }, - "sshpk": { - "version": "1.16.1", - "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz", - "integrity": "sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==", - "dev": true, - "requires": { - "asn1": "~0.2.3", - "assert-plus": "^1.0.0", - "bcrypt-pbkdf": "^1.0.0", - "dashdash": "^1.12.0", - "ecc-jsbn": "~0.1.1", - "getpass": "^0.1.1", - "jsbn": "~0.1.0", - "safer-buffer": "^2.0.2", - "tweetnacl": "~0.14.0" - } - }, - "ssri": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/ssri/-/ssri-6.0.1.tgz", - "integrity": "sha512-3Wge10hNcT1Kur4PDFwEieXSCMCJs/7WvSACcrMYrNp+b8kDL1/0wJch5Ni2WrtwEa2IO8OsVfeKIciKCDx/QA==", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", "dev": true, - "requires": { - "figgy-pudding": "^3.5.1" - } + "license": "BSD-3-Clause" }, - "static-extend": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz", - "integrity": "sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY=", + "node_modules/stdin-discarder": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/stdin-discarder/-/stdin-discarder-0.2.2.tgz", + "integrity": "sha512-UhDfHmA92YAlNnCfhmq0VeNL5bDbiZGg7sZ2IvPsXubGkiNa9EC+tUTsjBRsYUAz87btI6/1wf4XoVvQ3uRnmQ==", "dev": true, - "requires": { - "define-property": "^0.2.5", - "object-copy": "^0.1.0" + "license": "MIT", + "engines": { + "node": ">=18" }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/strict-event-emitter-types": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strict-event-emitter-types/-/strict-event-emitter-types-2.0.0.tgz", + "integrity": "sha512-Nk/brWYpD85WlOgzw5h173aci0Teyv8YdIAEtV+N88nDB0dLlazZyJMIsN6eo1/AR61l+p6CJTG1JIyFaoNEEA==", + "license": "ISC" + }, + "node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "license": "MIT", "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, - "requires": { - "is-descriptor": "^0.1.0" - } - } + "safe-buffer": "~5.1.0" } }, - "stream-browserify": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/stream-browserify/-/stream-browserify-2.0.2.tgz", - "integrity": "sha512-nX6hmklHs/gr2FuxYDltq8fJA1GDlxKQCz8O/IM4atRqBH8OORmBNgfvW5gG10GT/qQ9u0CzIvr2X5Pkt6ntqg==", + "node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", "dev": true, - "requires": { - "inherits": "~2.0.1", - "readable-stream": "^2.0.2" + "license": "MIT", + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", "dependencies": { - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", - "dev": true - }, - "readable-stream": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", - "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", - "dev": true, - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dev": true, - "requires": { - "safe-buffer": "~5.1.0" - } - } + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/string-width/node_modules/ansi-regex": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", + "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" } }, - "stream-each": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/stream-each/-/stream-each-1.2.3.tgz", - "integrity": "sha512-vlMC2f8I2u/bZGqkdfLQW/13Zihpej/7PmSiMQsbYddxuTsJp8vRe2x2FvVExZg7FaOds43ROAuFJwPR4MTZLw==", + "node_modules/string-width/node_modules/strip-ansi": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz", + "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==", "dev": true, - "requires": { - "end-of-stream": "^1.1.0", - "stream-shift": "^1.0.0" + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.2.2" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" } }, - "stream-http": { - "version": "2.8.3", - "resolved": "https://registry.npmjs.org/stream-http/-/stream-http-2.8.3.tgz", - "integrity": "sha512-+TSkfINHDo4J+ZobQLWiMouQYB+UVYFttRA94FpEzzJ7ZdqcL4uUUQ7WkdkI4DSozGmgBUE/a47L+38PenXhUw==", + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "dev": true, - "requires": { - "builtin-status-codes": "^3.0.0", - "inherits": "^2.0.1", - "readable-stream": "^2.3.6", - "to-arraybuffer": "^1.0.0", - "xtend": "^4.0.0" - }, + "license": "MIT", "dependencies": { - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", - "dev": true - }, - "readable-stream": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", - "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", - "dev": true, - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dev": true, - "requires": { - "safe-buffer": "~5.1.0" - } - } + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" } }, - "stream-shift": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.0.tgz", - "integrity": "sha1-1cdSgl5TZ+eG944Y5EXqIjoVWVI=", - "dev": true - }, - "string-width": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", - "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "dev": true, - "requires": { - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^4.0.0" + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" } }, - "string_decoder": { - "version": "0.10.31", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", - "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=", - "dev": true - }, - "strip-ansi": { + "node_modules/strip-final-newline": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-4.0.0.tgz", + "integrity": "sha512-aulFJcD6YK8V1G7iRB5tigAP4TsHBZZrOV8pjV++zdUwmeV8uzbY7yn6h9MswN62adStNZFuCIx4haBnRuMDaw==", "dev": true, - "requires": { - "ansi-regex": "^3.0.0" + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "strip-eof": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", - "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=", - "dev": true - }, - "strip-json-comments": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", - "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", - "dev": true - }, - "supports-color": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.0.0.tgz", - "integrity": "sha512-on9Kwidc1IUQo+bQdhi8+Tijpo0e1SS6RoGo2guUwn5vdaxw8RXOF9Vb2ws+ihWOmh4JnCJOvaziZWP1VABaLg==", + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", "dev": true, - "requires": { - "has-flag": "^3.0.0" + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "tapable": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/tapable/-/tapable-1.1.3.tgz", - "integrity": "sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA==", - "dev": true - }, - "term-size": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/term-size/-/term-size-1.2.0.tgz", - "integrity": "sha1-RYuDiH8oj8Vtb/+/rSYuJmOO+mk=", + "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==", "dev": true, - "requires": { - "execa": "^0.7.0" + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" } }, - "terser": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/terser/-/terser-4.3.4.tgz", - "integrity": "sha512-Kcrn3RiW8NtHBP0ssOAzwa2MsIRQ8lJWiBG/K7JgqPlomA3mtb2DEmp4/hrUA+Jujx+WZ02zqd7GYD+QRBB/2Q==", + "node_modules/table": { + "version": "6.9.0", + "resolved": "https://registry.npmjs.org/table/-/table-6.9.0.tgz", + "integrity": "sha512-9kY+CygyYM6j02t5YFHbNz2FN5QmYGv9zAjVp4lCDjlCw7amdckXlEt/bjMhUIfj4ThGRE4gCUH5+yGnNuPo5A==", "dev": true, - "requires": { - "commander": "^2.20.0", - "source-map": "~0.6.1", - "source-map-support": "~0.5.12" - }, + "license": "BSD-3-Clause", "dependencies": { - "commander": { - "version": "2.20.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.1.tgz", - "integrity": "sha512-cCuLsMhJeWQ/ZpsFTbE765kvVfoeSddc4nU3up4fV+fDBcfUXnbITJ+JzhkdjzOqhURjZgujxaioam4RM9yGUg==", - "dev": true - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - }, - "source-map-support": { - "version": "0.5.13", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz", - "integrity": "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==", - "dev": true, - "requires": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" - } - } - } - }, - "terser-webpack-plugin": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-1.4.1.tgz", - "integrity": "sha512-ZXmmfiwtCLfz8WKZyYUuuHf3dMYEjg8NrjHMb0JqHVHVOSkzp3cW2/XG1fP3tRhqEqSzMwzzRQGtAPbs4Cncxg==", - "dev": true, - "requires": { - "cacache": "^12.0.2", - "find-cache-dir": "^2.1.0", - "is-wsl": "^1.1.0", - "schema-utils": "^1.0.0", - "serialize-javascript": "^1.7.0", - "source-map": "^0.6.1", - "terser": "^4.1.2", - "webpack-sources": "^1.4.0", - "worker-farm": "^1.7.0" - }, - "dependencies": { - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - } + "ajv": "^8.0.1", + "lodash.truncate": "^4.4.2", + "slice-ansi": "^4.0.0", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=10.0.0" } }, - "through2": { - "version": "0.6.5", - "resolved": "https://registry.npmjs.org/through2/-/through2-0.6.5.tgz", - "integrity": "sha1-QaucZ7KdVyCQcUEOHXp6lozTrUg=", + "node_modules/table/node_modules/ajv": { + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz", + "integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==", "dev": true, - "requires": { - "readable-stream": ">=1.0.33-1 <1.1.0-0", - "xtend": ">=4.0.0 <4.1.0-0" + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" } }, - "timed-out": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/timed-out/-/timed-out-4.0.1.tgz", - "integrity": "sha1-8y6srFoXW+ol1/q1Zas+2HQe9W8=", - "dev": true - }, - "timers-browserify": { - "version": "2.0.11", - "resolved": "https://registry.npmjs.org/timers-browserify/-/timers-browserify-2.0.11.tgz", - "integrity": "sha512-60aV6sgJ5YEbzUdn9c8kYGIqOubPoUdqQCul3SBAsRCZ40s6Y5cMcrW4dt3/k/EsbLVJNl9n6Vz3fTc+k2GeKQ==", + "node_modules/table/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", "dev": true, - "requires": { - "setimmediate": "^1.0.4" - } + "license": "MIT" }, - "to-arraybuffer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz", - "integrity": "sha1-fSKbH8xjfkZsoIEYCDanqr/4P0M=", - "dev": true + "node_modules/table/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true, + "license": "MIT" }, - "to-object-path": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz", - "integrity": "sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68=", + "node_modules/table/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, + "license": "MIT", "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" } }, - "to-regex": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/to-regex/-/to-regex-3.0.2.tgz", - "integrity": "sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==", + "node_modules/text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", "dev": true, - "requires": { - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "regex-not": "^1.0.2", - "safe-regex": "^1.1.0" - } + "license": "MIT" }, - "to-regex-range": { + "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==", "dev": true, - "requires": { + "license": "MIT", + "dependencies": { "is-number": "^7.0.0" - } - }, - "tough-cookie": { - "version": "2.4.3", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.4.3.tgz", - "integrity": "sha512-Q5srk/4vDM54WJsJio3XNn6K2sCG+CQ8G5Wz6bZhRZoAe/+TxjWB/GlFAnYEbkYVlON9FMk/fE3h2RLpPXo4lQ==", - "dev": true, - "requires": { - "psl": "^1.1.24", - "punycode": "^1.4.1" }, - "dependencies": { - "punycode": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", - "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=", - "dev": true - } - } - }, - "ts-loader": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/ts-loader/-/ts-loader-6.2.0.tgz", - "integrity": "sha512-Da8h3fD+HiZ9GvZJydqzk3mTC9nuOKYlJcpuk+Zv6Y1DPaMvBL+56GRzZFypx2cWrZFMsQr869+Ua2slGoLxvQ==", - "dev": true, - "requires": { - "chalk": "^2.3.0", - "enhanced-resolve": "^4.0.0", - "loader-utils": "^1.0.2", - "micromatch": "^4.0.0", - "semver": "^6.0.0" - }, - "dependencies": { - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true - } + "engines": { + "node": ">=8.0" } }, - "tslib": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.10.0.tgz", - "integrity": "sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ==", - "dev": true - }, - "tslint": { - "version": "5.20.0", - "resolved": "https://registry.npmjs.org/tslint/-/tslint-5.20.0.tgz", - "integrity": "sha512-2vqIvkMHbnx8acMogAERQ/IuINOq6DFqgF8/VDvhEkBqQh/x6SP0Y+OHnKth9/ZcHQSroOZwUQSN18v8KKF0/g==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.0.0", - "builtin-modules": "^1.1.1", - "chalk": "^2.3.0", - "commander": "^2.12.1", - "diff": "^4.0.1", - "glob": "^7.1.1", - "js-yaml": "^3.13.1", - "minimatch": "^3.0.4", - "mkdirp": "^0.5.1", - "resolve": "^1.3.2", - "semver": "^5.3.0", - "tslib": "^1.8.0", - "tsutils": "^2.29.0" - }, - "dependencies": { - "commander": { - "version": "2.20.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.1.tgz", - "integrity": "sha512-cCuLsMhJeWQ/ZpsFTbE765kvVfoeSddc4nU3up4fV+fDBcfUXnbITJ+JzhkdjzOqhURjZgujxaioam4RM9yGUg==", - "dev": true - }, - "diff": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.1.tgz", - "integrity": "sha512-s2+XdvhPCOF01LRQBC8hf4vhbVmI2CGS5aZnxLJlT5FtdhPCDFq80q++zK2KlrVorVDdL5BOGZ/VfLrVtYNF+Q==", - "dev": true - } - } + "node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", + "license": "MIT" }, - "tsutils": { - "version": "2.29.0", - "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-2.29.0.tgz", - "integrity": "sha512-g5JVHCIJwzfISaXpXE1qvNalca5Jwob6FjI4AoPlqMusJ6ftFE7IkkFoMhVLRgK+4Kx3gkzb8UZK5t5yTTvEmA==", + "node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", "dev": true, - "requires": { - "tslib": "^1.8.1" - } + "license": "0BSD" }, - "tty-browserify": { - "version": "0.0.0", - "resolved": "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.0.tgz", - "integrity": "sha1-oVe6QC2iTpv5V/mqadUk7tQpAaY=", - "dev": true - }, - "tunnel-agent": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", - "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", + "node_modules/tsutils": { + "version": "3.21.0", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", + "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", "dev": true, - "requires": { - "safe-buffer": "^5.0.1" + "license": "MIT", + "dependencies": { + "tslib": "^1.8.1" + }, + "engines": { + "node": ">= 6" + }, + "peerDependencies": { + "typescript": ">=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta" } }, - "tweetnacl": { - "version": "0.14.5", - "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", - "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=", - "dev": true - }, - "typedarray": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", - "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=", - "dev": true - }, - "typescript": { - "version": "3.6.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.6.3.tgz", - "integrity": "sha512-N7bceJL1CtRQ2RiG0AQME13ksR7DiuQh/QehubYcghzv20tnh+MQnQIuJddTmsbqYj+dztchykemz0zFzlvdQw==", - "dev": true - }, - "union-value": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz", - "integrity": "sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg==", + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", "dev": true, - "requires": { - "arr-union": "^3.1.0", - "get-value": "^2.0.6", - "is-extendable": "^0.1.1", - "set-value": "^2.0.1" + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" } }, - "unique-filename": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-1.1.1.tgz", - "integrity": "sha512-Vmp0jIp2ln35UTXuryvjzkjGdRyf9b2lTXuSYUiPmzRcl3FDtYqAwOnTJkAngD9SWhnoJzDbTKwaOrZ+STtxNQ==", + "node_modules/type-detect": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", "dev": true, - "requires": { - "unique-slug": "^2.0.0" + "license": "MIT", + "engines": { + "node": ">=4" } }, - "unique-slug": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-2.0.2.tgz", - "integrity": "sha512-zoWr9ObaxALD3DOPfjPSqxt4fnZiWblxHIgeWqW8x7UqDzEtHEQLzji2cuJYQFCU6KmoJikOYAZlrTHHebjx2w==", + "node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", "dev": true, - "requires": { - "imurmurhash": "^0.1.4" + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "unique-string": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-1.0.0.tgz", - "integrity": "sha1-nhBXzKhRq7kzmPizOuGHuZyuwRo=", + "node_modules/typescript": { + "version": "4.9.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", + "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==", "dev": true, - "requires": { - "crypto-random-string": "^1.0.0" + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=4.2.0" } }, - "universalify": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", - "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==" + "node_modules/undici-types": { + "version": "5.26.5", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", + "license": "MIT" }, - "unset-value": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz", - "integrity": "sha1-g3aHP30jNRef+x5vw6jtDfyKtVk=", - "dev": true, - "requires": { - "has-value": "^0.3.1", - "isobject": "^3.0.0" - }, - "dependencies": { - "has-value": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/has-value/-/has-value-0.3.1.tgz", - "integrity": "sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8=", - "dev": true, - "requires": { - "get-value": "^2.0.3", - "has-values": "^0.1.4", - "isobject": "^2.0.0" - }, - "dependencies": { - "isobject": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", - "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", - "dev": true, - "requires": { - "isarray": "1.0.0" - } - } - } - }, - "has-values": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz", - "integrity": "sha1-bWHeldkd/Km5oCCJrThL/49it3E=", - "dev": true - }, - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", - "dev": true - } + "node_modules/unicorn-magic": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/unicorn-magic/-/unicorn-magic-0.3.0.tgz", + "integrity": "sha512-+QBBXBCvifc56fsbuxZQ6Sic3wqqc3WWaqxs58gvJrcOuN83HGTCwz3oS5phzU9LthRNE9VrJCFCLUgHeeFnfA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "unzip-response": { + "node_modules/universalify": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/unzip-response/-/unzip-response-2.0.1.tgz", - "integrity": "sha1-0vD3N9FrBhXnKmk17QQhRXLVb5c=", - "dev": true - }, - "upath": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/upath/-/upath-1.2.0.tgz", - "integrity": "sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg==", - "dev": true + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } }, - "uri-js": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz", - "integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==", + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", "dev": true, - "requires": { + "license": "BSD-2-Clause", + "dependencies": { "punycode": "^2.1.0" } }, - "urix": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz", - "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=", - "dev": true + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "dev": true, + "license": "MIT" }, - "url": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/url/-/url-0.11.0.tgz", - "integrity": "sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE=", + "node_modules/v8-compile-cache": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.4.0.tgz", + "integrity": "sha512-ocyWc3bAHBB/guyqJQVI5o4BZkPhznPYUG2ea80Gond/BgNWpap8TOmLSeeQG7bnh2KMISxskdADG59j7zruhw==", "dev": true, - "requires": { - "punycode": "1.3.2", - "querystring": "0.2.0" - }, + "license": "MIT" + }, + "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==", + "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==", + "license": "MIT", "dependencies": { - "punycode": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", - "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=", - "dev": true - } + "minimatch": "^5.1.0", + "semver": "^7.3.7", + "vscode-languageserver-protocol": "3.17.5" + }, + "engines": { + "vscode": "^1.82.0" } }, - "url-parse": { - "version": "1.4.4", - "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.4.4.tgz", - "integrity": "sha512-/92DTTorg4JjktLNLe6GPS2/RvAd/RGr6LuktmWSMLEOa6rjnlrFXNgSbSmkNvCoL2T028A0a1JaJLzRMlFoHg==", - "dev": true, - "requires": { - "querystringify": "^2.0.0", - "requires-port": "^1.0.0" + "node_modules/vscode-languageclient/node_modules/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" } }, - "url-parse-lax": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-1.0.0.tgz", - "integrity": "sha1-evjzA2Rem9eaJy56FKxovAYJ2nM=", - "dev": true, - "requires": { - "prepend-http": "^1.0.1" + "node_modules/vscode-languageclient/node_modules/minimatch": { + "version": "5.1.9", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.9.tgz", + "integrity": "sha512-7o1wEA2RyMP7Iu7GNba9vc0RWWGACJOCZBJX2GJWip0ikV+wcOsgVuY9uE8CPiyQhkGFSlhuSkZPavN7u1c2Fw==", + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" } }, - "use": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz", - "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==", - "dev": true - }, - "util": { - "version": "0.10.3", - "resolved": "https://registry.npmjs.org/util/-/util-0.10.3.tgz", - "integrity": "sha1-evsa/lCAUkZInj23/g7TeTNqwPk=", - "requires": { - "inherits": "2.0.1" - }, - "dependencies": { - "inherits": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz", - "integrity": "sha1-sX0I0ya0Qj5Wjv9xn5GwscvfafE=" - } + "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==", + "license": "MIT", + "dependencies": { + "vscode-jsonrpc": "8.2.0", + "vscode-languageserver-types": "3.17.5" } }, - "util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", - "dev": true + "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==", + "license": "MIT" }, - "uuid": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz", - "integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==", - "dev": true + "node_modules/vsls": { + "version": "1.0.4753", + "resolved": "https://registry.npmjs.org/vsls/-/vsls-1.0.4753.tgz", + "integrity": "sha512-hmrsMbhjuLoU8GgtVfqhbV4ZkGvDpLV2AFmzx+cCOGNra2qk0Q36dYkfwENqy/vJVQ/2/lhxcn+69FYnKQRhgg==", + "license": "SEE LICENSE IN LICENSE.txt", + "dependencies": { + "@microsoft/servicehub-framework": "^2.6.74" + } }, - "v8-compile-cache": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.0.3.tgz", - "integrity": "sha512-CNmdbwQMBjwr9Gsmohvm0pbL954tJrNzf6gWL3K+QMQf00PF7ERGrEiLgjuU3mKreLC2MeGhUsNV9ybTbLgd3w==", - "dev": true + "node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", + "license": "BSD-2-Clause" }, - "verror": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", - "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", - "dev": true, - "requires": { - "assert-plus": "^1.0.0", - "core-util-is": "1.0.2", - "extsprintf": "^1.2.0" + "node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "license": "MIT", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" } }, - "vm-browserify": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-1.1.0.tgz", - "integrity": "sha512-iq+S7vZJE60yejDYM0ek6zg308+UZsdtPExWP9VZoCFCz1zkJoXFnAX7aZfd/ZwrkidzdUZL0C/ryW+JwAiIGw==", - "dev": true - }, - "vscode": { - "version": "1.1.33", - "resolved": "https://registry.npmjs.org/vscode/-/vscode-1.1.33.tgz", - "integrity": "sha512-sXedp2oF6y4ZvqrrFiZpeMzaCLSWV+PpYkIxjG/iYquNZ9KrLL2LujltGxPLvzn49xu2sZkyC+avVNFgcJD1Iw==", - "dev": true, - "requires": { - "glob": "^7.1.2", - "mocha": "^4.0.1", - "request": "^2.88.0", - "semver": "^5.4.1", - "source-map-support": "^0.5.0", - "url-parse": "^1.4.4", - "vscode-test": "^0.1.4" - }, - "dependencies": { - "browser-stdout": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.0.tgz", - "integrity": "sha1-81HTKWnTL6XXpVZxVCY9korjvR8=", - "dev": true - }, - "debug": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - }, - "diff": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/diff/-/diff-3.3.1.tgz", - "integrity": "sha512-MKPHZDMB0o6yHyDryUOScqZibp914ksXwAMYMTHj6KO8UeKsRYNJD3oNCKjTqZon+V488P7N/HzXF8t7ZR95ww==", - "dev": true - }, - "growl": { - "version": "1.10.3", - "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.3.tgz", - "integrity": "sha512-hKlsbA5Vu3xsh1Cg3J7jSmX/WaW6A5oBeqzM88oNbCRQFz+zUaXm6yxS4RVytp1scBoJzSYl4YAEOQIt6O8V1Q==", - "dev": true - }, - "has-flag": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-2.0.0.tgz", - "integrity": "sha1-6CB68cx7MNRGzHC3NLXovhj4jVE=", - "dev": true - }, - "he": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/he/-/he-1.1.1.tgz", - "integrity": "sha1-k0EP0hsAlzUVH4howvJx80J+I/0=", - "dev": true - }, - "mocha": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-4.1.0.tgz", - "integrity": "sha512-0RVnjg1HJsXY2YFDoTNzcc1NKhYuXKRrBAG2gDygmJJA136Cs2QlRliZG1mA0ap7cuaT30mw16luAeln+4RiNA==", - "dev": true, - "requires": { - "browser-stdout": "1.3.0", - "commander": "2.11.0", - "debug": "3.1.0", - "diff": "3.3.1", - "escape-string-regexp": "1.0.5", - "glob": "7.1.2", - "growl": "1.10.3", - "he": "1.1.1", - "mkdirp": "0.5.1", - "supports-color": "4.4.0" - }, - "dependencies": { - "glob": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", - "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", - "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - } - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true - }, - "supports-color": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-4.4.0.tgz", - "integrity": "sha512-rKC3+DyXWgK0ZLKwmRsrkyHVZAjNkfzeehuFWdGGcqGDTZFH73+RH6S/RDAAxl9GusSjZSUWYLmT9N5pzXFOXQ==", - "dev": true, - "requires": { - "has-flag": "^2.0.0" - } - } + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" } }, - "vscode-test": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/vscode-test/-/vscode-test-0.1.5.tgz", - "integrity": "sha512-s+lbF1Dtasc0yXVB9iQTexBe2JK6HJAUJe3fWezHKIjq+xRw5ZwCMEMBaonFIPy7s95qg2HPTRDR5W4h4kbxGw==", - "dev": true, - "requires": { - "http-proxy-agent": "^2.1.0", - "https-proxy-agent": "^2.2.1" - } - }, - "watchpack": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-1.6.0.tgz", - "integrity": "sha512-i6dHe3EyLjMmDlU1/bGQpEw25XSjkJULPuAVKCbNRefQVq48yXKUpwg538F7AZTf9kyr57zj++pQFltUa5H7yA==", - "dev": true, - "requires": { - "chokidar": "^2.0.2", - "graceful-fs": "^4.1.2", - "neo-async": "^2.5.0" - } - }, - "webpack": { - "version": "4.41.0", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-4.41.0.tgz", - "integrity": "sha512-yNV98U4r7wX1VJAj5kyMsu36T8RPPQntcb5fJLOsMz/pt/WrKC0Vp1bAlqPLkA1LegSwQwf6P+kAbyhRKVQ72g==", - "dev": true, - "requires": { - "@webassemblyjs/ast": "1.8.5", - "@webassemblyjs/helper-module-context": "1.8.5", - "@webassemblyjs/wasm-edit": "1.8.5", - "@webassemblyjs/wasm-parser": "1.8.5", - "acorn": "^6.2.1", - "ajv": "^6.10.2", - "ajv-keywords": "^3.4.1", - "chrome-trace-event": "^1.0.2", - "enhanced-resolve": "^4.1.0", - "eslint-scope": "^4.0.3", - "json-parse-better-errors": "^1.0.2", - "loader-runner": "^2.4.0", - "loader-utils": "^1.2.3", - "memory-fs": "^0.4.1", - "micromatch": "^3.1.10", - "mkdirp": "^0.5.1", - "neo-async": "^2.6.1", - "node-libs-browser": "^2.2.1", - "schema-utils": "^1.0.0", - "tapable": "^1.1.3", - "terser-webpack-plugin": "^1.4.1", - "watchpack": "^1.6.0", - "webpack-sources": "^1.4.1" - }, - "dependencies": { - "ajv": { - "version": "6.10.2", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.10.2.tgz", - "integrity": "sha512-TXtUUEYHuaTEbLZWIKUr5pmBuhDLy+8KYtPYdcV8qC+pOZL+NKqYwvWSRrVXHn+ZmRRAu8vJTAznH7Oag6RVRw==", - "dev": true, - "requires": { - "fast-deep-equal": "^2.0.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - } - }, - "braces": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", - "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", - "dev": true, - "requires": { - "arr-flatten": "^1.1.0", - "array-unique": "^0.3.2", - "extend-shallow": "^2.0.1", - "fill-range": "^4.0.0", - "isobject": "^3.0.1", - "repeat-element": "^1.1.2", - "snapdragon": "^0.8.1", - "snapdragon-node": "^2.0.1", - "split-string": "^3.0.2", - "to-regex": "^3.0.1" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "fill-range": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", - "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", - "dev": true, - "requires": { - "extend-shallow": "^2.0.1", - "is-number": "^3.0.0", - "repeat-string": "^1.6.1", - "to-regex-range": "^2.1.0" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "is-number": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", - "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "micromatch": { - "version": "3.1.10", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", - "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", - "dev": true, - "requires": { - "arr-diff": "^4.0.0", - "array-unique": "^0.3.2", - "braces": "^2.3.1", - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "extglob": "^2.0.4", - "fragment-cache": "^0.2.1", - "kind-of": "^6.0.2", - "nanomatch": "^1.2.9", - "object.pick": "^1.3.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.2" - } - }, - "to-regex-range": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", - "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", - "dev": true, - "requires": { - "is-number": "^3.0.0", - "repeat-string": "^1.6.1" - } - } - } + "node_modules/winreg": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/winreg/-/winreg-1.2.5.tgz", + "integrity": "sha512-uf7tHf+tw0B1y+x+mKTLHkykBgK2KMs3g+KlzmyMbLvICSHQyB/xOFjTT8qZ3oeTFyU7Bbj4FzXitGG6jvKhYw==", + "license": "BSD-2-Clause" }, - "webpack-cli": { - "version": "3.3.9", - "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-3.3.9.tgz", - "integrity": "sha512-xwnSxWl8nZtBl/AFJCOn9pG7s5CYUYdZxmmukv+fAHLcBIHM36dImfpQg3WfShZXeArkWlf6QRw24Klcsv8a5A==", - "dev": true, - "requires": { - "chalk": "2.4.2", - "cross-spawn": "6.0.5", - "enhanced-resolve": "4.1.0", - "findup-sync": "3.0.0", - "global-modules": "2.0.0", - "import-local": "2.0.0", - "interpret": "1.2.0", - "loader-utils": "1.2.3", - "supports-color": "6.1.0", - "v8-compile-cache": "2.0.3", - "yargs": "13.2.4" - }, - "dependencies": { - "ansi-regex": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", - "dev": true - }, - "camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", - "dev": true - }, - "cliui": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz", - "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==", - "dev": true, - "requires": { - "string-width": "^3.1.0", - "strip-ansi": "^5.2.0", - "wrap-ansi": "^5.1.0" - } - }, - "cross-spawn": { - "version": "6.0.5", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", - "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", - "dev": true, - "requires": { - "nice-try": "^1.0.4", - "path-key": "^2.0.1", - "semver": "^5.5.0", - "shebang-command": "^1.2.0", - "which": "^1.2.9" - } - }, - "get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "dev": true - }, - "require-main-filename": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", - "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", - "dev": true - }, - "string-width": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", - "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", - "dev": true, - "requires": { - "emoji-regex": "^7.0.1", - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^5.1.0" - } - }, - "strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", - "dev": true, - "requires": { - "ansi-regex": "^4.1.0" - } - }, - "supports-color": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", - "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - }, - "wrap-ansi": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz", - "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.0", - "string-width": "^3.0.0", - "strip-ansi": "^5.0.0" - } - }, - "yargs": { - "version": "13.2.4", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.2.4.tgz", - "integrity": "sha512-HG/DWAJa1PAnHT9JAhNa8AbAv3FPaiLzioSjCcmuXXhP8MlpHO5vwls4g4j6n30Z74GVQj8Xa62dWVx1QCGklg==", - "dev": true, - "requires": { - "cliui": "^5.0.0", - "find-up": "^3.0.0", - "get-caller-file": "^2.0.1", - "os-locale": "^3.1.0", - "require-directory": "^2.1.1", - "require-main-filename": "^2.0.0", - "set-blocking": "^2.0.0", - "string-width": "^3.0.0", - "which-module": "^2.0.0", - "y18n": "^4.0.0", - "yargs-parser": "^13.1.0" - } - }, - "yargs-parser": { - "version": "13.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.1.tgz", - "integrity": "sha512-oVAVsHz6uFrg3XQheFII8ESO2ssAf9luWuAd6Wexsu4F3OtIW0o8IribPXYrD4WC24LWtPrJlGy87y5udK+dxQ==", - "dev": true, - "requires": { - "camelcase": "^5.0.0", - "decamelize": "^1.2.0" - } - } + "node_modules/word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" } }, - "webpack-sources": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-1.4.3.tgz", - "integrity": "sha512-lgTS3Xhv1lCOKo7SA5TjKXMjpSM4sBjNV5+q2bqesbSPs5FjGmU6jjtBSkX9b4qW87vDIsCIlUPOEhbZrMdjeQ==", + "node_modules/workerpool": { + "version": "9.3.4", + "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-9.3.4.tgz", + "integrity": "sha512-TmPRQYYSAnnDiEB0P/Ytip7bFGvqnSU6I2BcuSw7Hx+JSg/DsUi5ebYfc8GYaSdpuvOcEs6dXxPurOYpe9QFwg==", "dev": true, - "requires": { - "source-list-map": "^2.0.0", - "source-map": "~0.6.1" - }, + "license": "Apache-2.0" + }, + "node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "dev": true, + "license": "MIT", "dependencies": { - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - } + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, - "which": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/which/-/which-1.3.0.tgz", - "integrity": "sha512-xcJpopdamTuY5duC/KnTTNBraPK54YwpenP4lzxU8H91GudWpFv38u0CKjclE1Wi2EH2EDz5LRcHcKbCIzqGyg==", + "node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", "dev": true, - "requires": { - "isexe": "^2.0.0" + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, - "which-module": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", - "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", - "dev": true - }, - "wide-align": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", - "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", + "node_modules/wrap-ansi-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", "dev": true, - "requires": { - "string-width": "^1.0.2 || 2" - } + "license": "MIT" }, - "widest-line": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-2.0.0.tgz", - "integrity": "sha1-AUKk6KJD+IgsAjOqDgKBqnYVInM=", - "dev": true, - "requires": { - "string-width": "^2.1.1" - } - }, - "wildglob": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/wildglob/-/wildglob-0.1.1.tgz", - "integrity": "sha1-Dz+i9HHeSOwiSW3Vee2o+5Oqeto=", - "dev": true, - "requires": { - "glob-parse": "0.0.1", - "microee": "0.0.6", - "minimatch": "~3.0.3", - "miniq": "~1.0.0", - "mm-brace-expand": "0.0.1", - "through2": "~0.6.5" - }, - "dependencies": { - "minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "dev": true, - "requires": { - "brace-expansion": "^1.1.7" - } - } + "node_modules/wrap-ansi-cjs/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" } }, - "winattr": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/winattr/-/winattr-3.0.0.tgz", - "integrity": "sha512-dt33rYsTYcGbB+I1ubB6ZLODibRSCW//TgY/SuajLllR9kHnHnbUMqnXIe0osYsXUdRLGs770zb3t9z/ScGUpw==", - "requires": { - "fswin": "^3.18.918" + "node_modules/wrap-ansi/node_modules/ansi-regex": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", + "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" } }, - "worker-farm": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/worker-farm/-/worker-farm-1.7.0.tgz", - "integrity": "sha512-rvw3QTZc8lAxyVrqcSGVm5yP/IJ2UcB3U0graE3LCFoZ0Yn2x4EoVSqJKdB/T5M+FLcRPjz4TDacRf3OCfNUzw==", + "node_modules/wrap-ansi/node_modules/ansi-styles": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", + "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", "dev": true, - "requires": { - "errno": "~0.1.7" + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "wrap-ansi": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", - "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=", + "node_modules/wrap-ansi/node_modules/strip-ansi": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz", + "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==", "dev": true, - "requires": { - "string-width": "^1.0.1", - "strip-ansi": "^3.0.1" - }, + "license": "MIT", "dependencies": { - "ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", - "dev": true - }, - "is-fullwidth-code-point": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", - "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", - "dev": true, - "requires": { - "number-is-nan": "^1.0.0" - } - }, - "string-width": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", - "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", - "dev": true, - "requires": { - "code-point-at": "^1.0.0", - "is-fullwidth-code-point": "^1.0.0", - "strip-ansi": "^3.0.0" - } - }, - "strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", - "dev": true, - "requires": { - "ansi-regex": "^2.0.0" - } - } + "ansi-regex": "^6.2.2" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" } }, - "wrappy": { + "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", - "dev": true - }, - "write-file-atomic": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-2.3.0.tgz", - "integrity": "sha512-xuPeK4OdjWqtfi59ylvVL0Yn35SF3zgcAcv7rBPFHVaEapaDr4GdGgm3j7ckTwH9wHL7fGmgfAnb0+THrHb8tA==", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", "dev": true, - "requires": { - "graceful-fs": "^4.1.11", - "imurmurhash": "^0.1.4", - "signal-exit": "^3.0.2" + "license": "ISC" + }, + "node_modules/ws": { + "version": "8.19.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.19.0.tgz", + "integrity": "sha512-blAT2mjOEIi0ZzruJfIhb3nps74PRWTCz1IjglWEEpQl5XS/UNama6u2/rjFkDDouqr4L67ry+1aGIALViWjDg==", + "license": "MIT", + "engines": { + "node": ">=10.0.0" }, - "dependencies": { - "graceful-fs": { - "version": "4.1.11", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", - "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=", - "dev": true + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true } } }, - "xdg-basedir": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-3.0.0.tgz", - "integrity": "sha1-SWsswQnsqNus/i3HK2A8F8WHCtQ=", - "dev": true - }, - "xml-js": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/xml-js/-/xml-js-1.4.1.tgz", - "integrity": "sha1-s0/6zgxtiYBKrwoYKCGsHtCKAek=", + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", "dev": true, - "requires": { - "sax": "^1.2.1" + "license": "ISC", + "engines": { + "node": ">=10" } }, - "xmlbuilder": { - "version": "8.2.2", - "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-8.2.2.tgz", - "integrity": "sha1-aSSGc0ELS6QuGmE2VR0pIjNap3M=", - "dev": true - }, - "xmldom": { - "version": "0.1.27", - "resolved": "https://registry.npmjs.org/xmldom/-/xmldom-0.1.27.tgz", - "integrity": "sha1-1QH5ezvbQDr4757MIFcxh6rawOk=", - "dev": true - }, - "xtend": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz", - "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68=", - "dev": true + "node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } }, - "y18n": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz", - "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==", - "dev": true + "node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=12" + } }, - "yallist": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", - "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=", - "dev": true + "node_modules/yargs-unparser": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz", + "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==", + "dev": true, + "license": "MIT", + "dependencies": { + "camelcase": "^6.0.0", + "decamelize": "^4.0.0", + "flat": "^5.0.2", + "is-plain-obj": "^2.1.0" + }, + "engines": { + "node": ">=10" + } }, - "yamljs": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/yamljs/-/yamljs-0.3.0.tgz", - "integrity": "sha512-C/FsVVhht4iPQYXOInoxUM/1ELSf9EsgKH34FofQOp6hwCPrW4vG4w5++TED3xRUo8gD7l0P1J1dLlDYzODsTQ==", + "node_modules/yargs-unparser/node_modules/is-plain-obj": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", + "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", "dev": true, - "requires": { - "argparse": "^1.0.7", - "glob": "^7.0.5" - }, - "dependencies": { - "glob": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", - "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", - "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "inherits": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", - "dev": true - }, - "minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "dev": true, - "requires": { - "brace-expansion": "^1.1.7" - } - } + "license": "MIT", + "engines": { + "node": ">=8" } }, - "yargs": { - "version": "12.0.5", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-12.0.5.tgz", - "integrity": "sha512-Lhz8TLaYnxq/2ObqHDql8dX8CJi97oHxrjUcYtzKbbykPtVW9WB+poxI+NM2UIzsMgNCZTIf0AQwsjK5yMAqZw==", + "node_modules/yargs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", "dev": true, - "requires": { - "cliui": "^4.0.0", - "decamelize": "^1.2.0", - "find-up": "^3.0.0", - "get-caller-file": "^1.0.1", - "os-locale": "^3.0.0", - "require-directory": "^2.1.1", - "require-main-filename": "^1.0.1", - "set-blocking": "^2.0.0", - "string-width": "^2.0.0", - "which-module": "^2.0.0", - "y18n": "^3.2.1 || ^4.0.0", - "yargs-parser": "^11.1.1" + "license": "MIT" + }, + "node_modules/yargs/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" } }, - "yargs-parser": { - "version": "11.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-11.1.1.tgz", - "integrity": "sha512-C6kB/WJDiaxONLJQnF8ccx9SEeoTTLek8RVbaOIsrAUS8VrBEXfmeSnCZxygc+XC2sNMBIwOOnfcxiynjHsVSQ==", + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", "dev": true, - "requires": { - "camelcase": "^5.0.0", - "decamelize": "^1.2.0" + "license": "MIT", + "engines": { + "node": ">=10" }, - "dependencies": { - "camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", - "dev": true - } + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "yargs-unparser": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-1.5.0.tgz", - "integrity": "sha512-HK25qidFTCVuj/D1VfNiEndpLIeJN78aqgR23nL3y4N0U/91cOAzqfHlF8n2BvoNDcZmJKin3ddNSvOxSr8flw==", + "node_modules/yoctocolors": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/yoctocolors/-/yoctocolors-2.1.2.tgz", + "integrity": "sha512-CzhO+pFNo8ajLM2d2IW/R93ipy99LWjtwblvC1RsoSUMZgyLbYFr221TnSNT7GjGdYui6P459mw9JH/g/zW2ug==", "dev": true, - "requires": { - "flat": "^4.1.0", - "lodash": "^4.17.11", - "yargs": "^12.0.5" + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } } } diff --git a/package.json b/package.json index 8eff4086f..d9d5fc636 100644 --- a/package.json +++ b/package.json @@ -1,18 +1,18 @@ { "name": "r", "displayName": "R", - "description": "Support for R language(run,syntax,snippet)", - "version": "1.1.5", - "author": "Yuki Ueda", + "description": "R Extension for Visual Studio Code", + "version": "2.8.8", + "author": "REditorSupport", "license": "SEE LICENSE IN LICENSE", - "publisher": "Ikuyadeu", + "publisher": "REditorSupport", "icon": "images/Rlogo.png", "repository": { "type": "git", - "url": "https://github.com/Ikuyadeu/vscode-R" + "url": "https://github.com/REditorSupport/vscode-R" }, "bugs": { - "url": "https://github.com/Ikuyadeu/vscode-R/issues" + "url": "https://github.com/REditorSupport/vscode-R/issues" }, "categories": [ "Programming Languages", @@ -26,40 +26,80 @@ "R Markdown" ], "engines": { - "vscode": "^1.29.0" + "vscode": "^1.75.0" }, "activationEvents": [ - "onLanguage:r", - "workspaceContains:*.r|.rd|.rmd", - "onCommand:r.createRTerm", - "onCommand:r.runSource", - "onCommand:r.knitRmd", - "onCommand:r.knitRmdToPdf", - "onCommand:r.knitRmdToHtml", - "onCommand:r.knitRmdToAll", - "onCommand:r.runSourcewithEcho", - "onCommand:r.runSelection", + "workspaceContains:**/*.{rproj,Rproj,r,R,rd,Rd,rmd,Rmd}", "onCommand:r.runSelectionInActiveTerm", - "onCommand:r.createGitignore" + "onWebviewPanel:rhelp" ], "main": "./dist/extension", "contributes": { - "languages": [ + "terminal": { + "profiles": [ + { + "title": "R Terminal", + "id": "r.terminal-profile" + } + ] + }, + "viewsContainers": { + "activitybar": [ + { + "id": "workspaceViewer", + "title": "R", + "icon": "./images/Rlogo.svg", + "when": "r.isActive" + } + ] + }, + "views": { + "workspaceViewer": [ + { + "id": "workspaceViewer", + "name": "Workspace", + "icon": "./images/Rlogo.svg", + "contextualTitle": "R" + }, + { + "id": "rHelpPages", + "name": "Help Pages", + "icon": "./images/Rlogo.svg", + "contextualTitle": "R", + "when": "r.helpViewer.show" + }, + { + "id": "rLiveShare", + "name": "Live Share Controls", + "icon": "./images/Rlogo.svg", + "contextualTitle": "R", + "when": "r.WorkspaceViewer:show && !r.liveShare:isGuest", + "visibility": "collapsed" + } + ] + }, + "viewsWelcome": [ { - "id": "r", - "extensions": [ - ".r", - ".rhistory", - ".rprofile", - ".rt" - ], - "aliases": [ - "R", - "r" - ], - "firstLine": "^#!/.*\\bRscript\\b", - "configuration": "./r-configuration.json" + "view": "workspaceViewer", + "when": "!r.WorkspaceViewer:show", + "contents": "R workspace viewer requires that the [session watcher be enabled](https://github.com/REditorSupport/vscode-R/wiki/R-Session-watcher)." + }, + { + "view": "rHelpPages", + "contents": "R Help Pages" + }, + { + "view": "rLiveShare", + "contents": "R Live Share not active.", + "when": "!r.liveShare:aborted" }, + { + "view": "rLiveShare", + "contents": "Could not connect to Live Share service.", + "when": "r.liveShare:aborted" + } + ], + "languages": [ { "id": "rd", "extensions": [ @@ -69,18 +109,41 @@ "R documentation", "r documentation" ], - "configuration": "./rd-configuration.json" + "configuration": "./language-configuration/rd.json" }, { - "id": "rmd", + "id": "debian-control.r", "extensions": [ - ".rmd" + ".Rproj" + ], + "aliases": [ + "R DCF" + ], + "filenames": [ + "DESCRIPTION", + ".lintr" ], + "configuration": "./language-configuration/dcf.json" + }, + { + "id": "namespace.r", + "aliases": [ + "R NAMESPACE" + ], + "filenames": [ + "NAMESPACE" + ], + "configuration": "./language-configuration/rnamespace.json" + }, + { + "id": "buildignore.r", "aliases": [ - "R Markdown", - "r markdown" + "R ignore" + ], + "filenames": [ + ".Rbuildignore" ], - "configuration": "./rd-configuration.json" + "configuration": "./language-configuration/rbuildignore.json" } ], "snippets": [ @@ -94,27 +157,109 @@ }, { "language": "rmd", - "path": "./snippets/markdown.json" + "path": "./snippets/rmarkdown.json" } ], "grammars": [ - { - "language": "r", - "scopeName": "source.r", - "path": "./syntax/r.json" - }, { "language": "rd", "scopeName": "text.tex.latex.rd", - "path": "./syntax/Rd (R Documentation).tmLanguage" + "path": "./syntaxes/rd.json" }, { - "language": "rmd", - "scopeName": "text.html.markdown.redcarpet", - "path": "./syntax/Markdown Redcarpet.tmLanguage" + "language": "debian-control.r", + "scopeName": "debian-control.r", + "path": "./syntaxes/dcf.json", + "embeddedLanguages": { + "meta.embedded.block.r": "r" + } + }, + { + "path": "./syntaxes/Rcpp.json", + "scopeName": "comment.block.r", + "injectTo": [ + "source.cpp" + ] + }, + { + "language": "namespace.r", + "scopeName": "namespace.r", + "path": "./syntaxes/rnamespace.json" + }, + { + "language": "buildignore.r", + "scopeName": "buildignore.r", + "path": "./syntaxes/rbuildignore.json" } ], "commands": [ + { + "command": "r.workspaceViewer.refreshEntry", + "title": "Manual Refresh", + "icon": "$(refresh)", + "category": "R Workspace Viewer", + "enablement": "rSessionActive" + }, + { + "command": "r.workspaceViewer.view", + "title": "View", + "icon": "$(open-preview)", + "category": "R Workspace Viewer" + }, + { + "command": "r.workspaceViewer.clear", + "title": "Clear environment", + "icon": "$(clear-all)", + "category": "R Workspace Viewer", + "enablement": "rSessionActive" + }, + { + "command": "r.workspaceViewer.remove", + "title": "Remove", + "icon": "$(close)", + "category": "R Workspace Viewer" + }, + { + "command": "r.workspaceViewer.save", + "title": "Save workspace", + "icon": "$(save)", + "category": "R Workspace Viewer", + "enablement": "rSessionActive" + }, + { + "command": "r.workspaceViewer.load", + "title": "Load workspace", + "icon": "$(folder-opened)", + "category": "R Workspace Viewer" + }, + { + "command": "r.workspaceViewer.package.showQuickPick", + "title": "Show QuickPick", + "category": "R Workspace Viewer" + }, + { + "command": "r.browser.refresh", + "title": "Refresh", + "icon": "$(refresh)", + "category": "R" + }, + { + "command": "r.browser.openExternal", + "title": "Open in external browser", + "icon": "$(link-external)", + "category": "R" + }, + { + "command": "r.showHelp", + "title": "Show help", + "category": "R" + }, + { + "command": "editor.action.webvieweditor.showFind", + "title": "R: Find in WebView", + "icon": "$(search)", + "category": "R" + }, { "title": "Create R terminal", "category": "R", @@ -124,19 +269,13 @@ "title": "Run Source", "category": "R", "command": "r.runSource", - "icon": { - "light": "./images/DownloadFile.ico", - "dark": "./images/DownloadFile_inverse.ico" - } + "icon": "$(run)" }, { "title": "Knit Rmd", "category": "R", "command": "r.knitRmd", - "icon": { - "light": "./images/DownloadFile.ico", - "dark": "./images/DownloadFile_inverse.ico" - } + "icon": "$(run-below)" }, { "title": "Knit Rmd To PDF", @@ -163,11 +302,6 @@ "category": "R", "command": "r.runSelection" }, - { - "title": "Run Selection/Line in Active Terminal", - "category": "R", - "command": "r.runSelectionInActiveTerm" - }, { "title": "Show number of rows for selected object", "category": "R", @@ -193,11 +327,21 @@ "category": "R", "command": "r.names" }, + { + "title": "View selected object", + "category": "R", + "command": "r.view" + }, { "title": "Create gitignore", "category": "R", "command": "r.createGitignore" }, + { + "title": "Create .lintr to the workspace", + "category": "R", + "command": "r.createLintrConfig" + }, { "title": "Preview Dataframe", "category": "R", @@ -210,28 +354,441 @@ }, { "title": "Load All", - "category": "R", + "category": "R Package", "command": "r.loadAll" }, { - "title": "Test Package", - "category": "R", + "title": "Build", + "category": "R Package", + "command": "r.build" + }, + { + "title": "Build Binary", + "category": "R Package", + "command": "r.buildBinary" + }, + { + "title": "Check", + "category": "R Package", + "command": "r.check" + }, + { + "title": "Document", + "category": "R Package", + "command": "r.document" + }, + { + "title": "Install", + "category": "R Package", + "command": "r.install" + }, + { + "title": "Test", + "category": "R Package", "command": "r.test" }, { - "title": "Install Package", + "title": "Generate C/C++ Configuration", + "category": "R Package", + "command": "r.generateCCppProperties" + }, + { + "title": "Attach Active Terminal", "category": "R", - "command": "r.install" + "command": "r.attachActive" }, { - "title": "Build Package", + "title": "Run Command With Selection or Word in Terminal", "category": "R", - "command": "r.build" + "command": "r.runCommandWithSelectionOrWord" }, { - "title": "Document", + "title": "Run Command With Editor Path in Terminal", "category": "R", - "command": "r.document" + "command": "r.runCommandWithEditorPath" + }, + { + "title": "Run Command in Terminal", + "category": "R", + "command": "r.runCommand" + }, + { + "title": "Run from Beginning to Line", + "category": "R", + "command": "r.runFromBeginningToLine" + }, + { + "title": "Run from Line to End", + "category": "R", + "command": "r.runFromLineToEnd" + }, + { + "title": "Run Selection/Line (Retain Cursor)", + "category": "R", + "command": "r.runSelectionRetainCursor" + }, + { + "title": "Select Current Chunk", + "category": "R", + "command": "r.selectCurrentChunk" + }, + { + "title": "Run Current Chunk", + "category": "R", + "command": "r.runCurrentChunk" + }, + { + "title": "Run Current Chunk and Move to Next Chunk", + "category": "R", + "command": "r.runCurrentChunkAndMove" + }, + { + "title": "Run Previous Chunk", + "category": "R", + "command": "r.runPreviousChunk" + }, + { + "title": "Run Next Chunk", + "category": "R", + "command": "r.runNextChunk" + }, + { + "title": "Run Above Chunks", + "category": "R", + "command": "r.runAboveChunks" + }, + { + "title": "Run Current and Below Chunks", + "category": "R", + "command": "r.runCurrentAndBelowChunks" + }, + { + "title": "Run Below Chunks", + "category": "R", + "command": "r.runBelowChunks" + }, + { + "title": "Run All Chunks", + "category": "R", + "command": "r.runAllChunks" + }, + { + "title": "Go to Previous Chunk", + "category": "R", + "command": "r.goToPreviousChunk" + }, + { + "title": "Go to Next Chunk", + "category": "R", + "command": "r.goToNextChunk" + }, + { + "title": "New Draft", + "category": "R Markdown", + "command": "r.rmarkdown.newDraft" + }, + { + "title": "R Markdown (rmd)", + "category": "R Markdown", + "when": false, + "command": "r.rmarkdown.newFileDraft" + }, + { + "title": "R Document (r)", + "category": "R", + "when": false, + "command": "r.newFileDocument" + }, + { + "command": "r.rmarkdown.setKnitDirectory", + "title": "Set Knit directory", + "icon": "$(zap)", + "category": "R Markdown" + }, + { + "command": "r.rmarkdown.showPreviewToSide", + "title": "Open Preview to the Side", + "icon": "$(open-preview)", + "category": "R Markdown" + }, + { + "command": "r.rmarkdown.showPreview", + "title": "Open Preview", + "icon": "$(open-preview)", + "category": "R Markdown" + }, + { + "command": "r.rmarkdown.preview.refresh", + "title": "Refresh Preview", + "icon": "$(refresh)", + "category": "R Markdown" + }, + { + "command": "r.rmarkdown.preview.openExternal", + "title": "Open in External Browser", + "icon": "$(link-external)", + "category": "R Markdown" + }, + { + "command": "r.rmarkdown.preview.showSource", + "title": "Show Source", + "icon": "$(go-to-file)", + "category": "R Markdown" + }, + { + "title": "Toggle Style", + "category": "R Markdown", + "command": "r.rmarkdown.preview.toggleStyle", + "icon": "$(symbol-color)" + }, + { + "title": "Enable Auto Refresh", + "category": "R Markdown", + "command": "r.rmarkdown.preview.enableAutoRefresh", + "icon": "$(sync)" + }, + { + "title": "Disable Auto Refresh", + "category": "R Markdown", + "command": "r.rmarkdown.preview.disableAutoRefresh", + "icon": "$(sync-ignored)" + }, + { + "title": "Launch RStudio Addin", + "category": "R", + "command": "r.launchAddinPicker" + }, + { + "command": "r.helpPanel.back", + "title": "Go Back", + "category": "R Help Panel", + "enablement": "r.helpPanel.canGoBack", + "icon": "$(arrow-left)" + }, + { + "command": "r.helpPanel.forward", + "title": "Go Forward", + "category": "R Help Panel", + "enablement": "r.helpPanel.canGoForward", + "icon": "$(arrow-right)" + }, + { + "command": "r.helpPanel.openExternal", + "title": "Open in external browser", + "category": "R Help Panel", + "enablement": "r.helpPanel.canOpenExternal", + "icon": "$(link-external)" + }, + { + "title": "Search Package Topics", + "category": "R Help Panel", + "command": "r.helpPanel.searchPackage", + "icon": "$(search)" + }, + { + "title": "Open Topic in new Panel", + "category": "R Help Panel", + "command": "r.helpPanel.openInNewPanel", + "icon": "$(add)" + }, + { + "title": "Clear Cached Files", + "category": "R Help Panel", + "command": "r.helpPanel.clearCache", + "icon": "$(refresh)" + }, + { + "title": "Filter Packages", + "category": "R Help Panel", + "command": "r.helpPanel.filterPackages", + "icon": "$(filter)" + }, + { + "title": "Remove from favorites", + "category": "R Help Panel", + "command": "r.helpPanel.removeFromFavorites", + "icon": "$(star-full)" + }, + { + "title": "Add to favorites", + "category": "R Help Panel", + "command": "r.helpPanel.addToFavorites", + "icon": "$(star-empty)" + }, + { + "title": "Show all packages", + "category": "R Help Panel", + "command": "r.helpPanel.showAllPackages", + "icon": "$(star-full)" + }, + { + "title": "Show only favorites", + "category": "R Help Panel", + "command": "r.helpPanel.showOnlyFavorites", + "icon": "$(star-empty)" + }, + { + "title": "Remove Package", + "category": "R Help Panel", + "command": "r.helpPanel.removePackage", + "icon": "$(trash)" + }, + { + "title": "Update Installed Packages", + "category": "R Help Panel", + "command": "r.helpPanel.updateInstalledPackages", + "icon": "$(clone)" + }, + { + "title": "Update/reinstall Package", + "category": "R Help Panel", + "command": "r.helpPanel.updatePackage", + "icon": "$(cloud-download)" + }, + { + "title": "Install multiple packages", + "category": "R Help Panel", + "command": "r.helpPanel.installPackages", + "icon": "$(expand-all)" + }, + { + "title": "Show QuickPick", + "category": "R Help Panel", + "command": "r.helpPanel.showQuickPick", + "icon": "$(zap)" + }, + { + "title": "Summarize Identical Topics", + "category": "R Help Panel", + "command": "r.helpPanel.summarizeTopics", + "icon": "$(fold)" + }, + { + "title": "Don't Summarize Identical Topics", + "category": "R Help Panel", + "command": "r.helpPanel.unsummarizeTopics", + "icon": "$(unfold)" + }, + { + "title": "Open help for selection", + "category": "R Help Panel", + "command": "r.helpPanel.openForSelection" + }, + { + "title": "Open help for path", + "category": "R Help Panel", + "command": "r.helpPanel.openForPath" + }, + { + "command": "r.liveShare.toggle", + "category": "R Live Share", + "title": "Toggle" + }, + { + "command": "r.liveShare.retry", + "title": "Retry connection to Live Share service", + "category": "R Live Share", + "icon": "$(refresh)" + }, + { + "title": "Toggle Style", + "category": "R Plot", + "command": "r.plot.toggleStyle", + "icon": "$(symbol-color)" + }, + { + "title": "Toggle Full Window Mode", + "category": "R Plot", + "command": "r.plot.toggleFullWindow", + "icon": "$(screen-full)" + }, + { + "title": "Toggle Preview Plot Layout", + "category": "R Plot", + "command": "r.plot.togglePreviewPlots", + "icon": "$(list-flat)" + }, + { + "title": "Export Plot", + "category": "R Plot", + "command": "r.plot.exportPlot", + "icon": "$(clone)" + }, + { + "title": "Close Plot", + "category": "R Plot", + "command": "r.plot.closePlot", + "icon": "$(trash)" + }, + { + "title": "Hide Plot", + "category": "R Plot", + "command": "r.plot.hidePlot", + "icon": "$(close)" + }, + { + "title": "Restore Plots", + "category": "R Plot", + "command": "r.plot.resetPlots", + "icon": "$(discard)" + }, + { + "title": "Previous Plot", + "category": "R Plot", + "command": "r.plot.prevPlot", + "enablement": "r.plot.canGoBack", + "icon": "$(chevron-left)" + }, + { + "title": "Next Plot", + "category": "R Plot", + "command": "r.plot.nextPlot", + "enablement": "r.plot.canGoForward", + "icon": "$(chevron-right)" + }, + { + "title": "First Plot", + "category": "R Plot", + "command": "r.plot.firstPlot", + "enablement": "r.plot.canGoBack", + "icon": "$(arrow-left)" + }, + { + "title": "Last Plot", + "category": "R Plot", + "command": "r.plot.lastPlot", + "enablement": "r.plot.canGoForward", + "icon": "$(arrow-right)" + }, + { + "title": "Zoom In", + "category": "R Plot", + "command": "r.plot.zoomIn", + "icon": "$(zoom-in)" + }, + { + "title": "Zoom Out", + "category": "R Plot", + "command": "r.plot.zoomOut", + "icon": "$(zoom-out)" + }, + { + "title": "Show Viewers", + "category": "R Plot", + "command": "r.plot.showViewers", + "icon": "$(versions)" + }, + { + "title": "Open Httpgd Url", + "category": "R Plot", + "command": "r.plot.openUrl", + "icon": "$(link)" + }, + { + "title": "Open in External Browser", + "category": "R Plot", + "command": "r.plot.openExternal", + "icon": "$(link-external)" } ], "keybindings": [ @@ -248,34 +805,22 @@ "when": "editorTextFocus && editorLangId == 'rmd'" }, { - "command": "r.nrow", - "key": "Ctrl+1", - "mac": "cmd+1", + "command": "r.runSelectionRetainCursor", + "key": "alt+enter", + "mac": "option+enter", "when": "editorTextFocus && editorLangId == 'r'" }, { - "command": "r.length", - "key": "Ctrl+2", - "mac": "cmd+2", - "when": "editorTextFocus && editorLangId == 'r'" + "command": "r.runCurrentChunk", + "key": "Ctrl+shift+enter", + "mac": "cmd+shift+enter", + "when": "editorTextFocus && (editorLangId == 'rmd' || editorLangId == 'r')" }, { - "command": "r.head", - "key": "Ctrl+3", - "mac": "cmd+3", - "when": "editorTextFocus && editorLangId == 'r'" - }, - { - "command": "r.thead", - "key": "Ctrl+4", - "mac": "cmd+4", - "when": "editorTextFocus && editorLangId == 'r'" - }, - { - "command": "r.names", - "key": "Ctrl+5", - "mac": "cmd+5", - "when": "editorTextFocus && editorLangId == 'r'" + "command": "r.runAboveChunks", + "key": "Ctrl+alt+p", + "mac": "cmd+alt+p", + "when": "editorTextFocus && (editorLangId == 'rmd' || editorLangId == 'r')" }, { "command": "r.runSource", @@ -294,103 +839,1202 @@ "key": "shift+Ctrl+enter", "mac": "shift+cmd+enter", "when": "editorTextFocus && editorLangId == 'r'" + }, + { + "command": "r.runFromBeginningToLine", + "key": "Ctrl+alt+b", + "mac": "cmd+alt+b", + "when": "editorTextFocus && editorLangId == 'r'" + }, + { + "command": "r.runFromLineToEnd", + "key": "Ctrl+alt+e", + "mac": "cmd+alt+e", + "when": "editorTextFocus && editorLangId == 'r'" + }, + { + "command": "r.rmarkdown.showPreviewToSide", + "key": "Ctrl+k v", + "mac": "cmd+k v", + "when": "editorTextFocus && editorLangId == 'rmd'" + }, + { + "command": "r.rmarkdown.showPreview", + "key": "Ctrl+shift+v", + "mac": "cmd+shift+v", + "when": "editorTextFocus && editorLangId == 'rmd'" } ], "menus": { - "editor/title": [ + "commandPalette": [ + { + "command": "r.workspaceViewer.view", + "when": "false" + }, + { + "command": "r.workspaceViewer.remove", + "when": "false" + }, + { + "command": "r.workspaceViewer.package.showQuickPick", + "when": "false" + }, + { + "command": "r.helpPanel.back", + "when": "false" + }, + { + "command": "r.helpPanel.forward", + "when": "false" + }, + { + "command": "r.helpPanel.openExternal", + "when": "false" + }, + { + "command": "r.helpPanel.searchPackage", + "when": "false" + }, + { + "command": "r.helpPanel.openInNewPanel", + "when": "false" + }, + { + "command": "r.helpPanel.clearCache", + "when": "false" + }, + { + "command": "r.helpPanel.filterPackages", + "when": "false" + }, + { + "command": "r.helpPanel.removeFromFavorites", + "when": "false" + }, + { + "command": "r.helpPanel.addToFavorites", + "when": "false" + }, + { + "command": "r.helpPanel.showAllPackages", + "when": "false" + }, + { + "command": "r.helpPanel.showOnlyFavorites", + "when": "false" + }, + { + "command": "r.helpPanel.removePackage", + "when": "false" + }, + { + "command": "r.helpPanel.updateInstalledPackages", + "when": "false" + }, + { + "command": "r.helpPanel.updatePackage", + "when": "false" + }, + { + "command": "r.helpPanel.installPackages", + "when": "false" + }, + { + "command": "r.helpPanel.showQuickPick", + "when": "false" + }, + { + "command": "r.helpPanel.summarizeTopics", + "when": "false" + }, + { + "command": "r.helpPanel.unsummarizeTopics", + "when": "false" + }, + { + "command": "r.helpPanel.openForPath", + "when": "false" + }, + { + "command": "r.liveShare.toggle", + "when": "false" + } + ], + "editor/title/run": [ { "when": "editorLangId == r", - "command": "r.runSource", - "group": "navigation" + "command": "r.runSource" }, { - "when": "editorLangId == rmd", + "when": "editorLangId == rmd && editorFocus", "command": "r.knitRmd", "group": "navigation" } + ], + "editor/title": [ + { + "command": "r.rmarkdown.preview.openExternal", + "when": "resourceScheme =~ /webview/ && r.rmarkdown.preview.active", + "group": "navigation@1" + }, + { + "command": "r.rmarkdown.preview.showSource", + "when": "resourceScheme =~ /webview/ && r.rmarkdown.preview.active", + "group": "navigation@2" + }, + { + "command": "r.rmarkdown.preview.toggleStyle", + "when": "resourceScheme =~ /webview/ && r.rmarkdown.preview.active", + "group": "navigation@3" + }, + { + "command": "r.rmarkdown.preview.refresh", + "when": "resourceScheme =~ /webview/ && r.rmarkdown.preview.active", + "group": "navigation@4" + }, + { + "command": "r.rmarkdown.preview.enableAutoRefresh", + "when": "resourceScheme =~ /webview/ && r.rmarkdown.preview.active && !r.rmarkdown.preview.autoRefresh", + "group": "navigation@5" + }, + { + "command": "r.rmarkdown.preview.disableAutoRefresh", + "when": "resourceScheme =~ /webview/ && r.rmarkdown.preview.active && r.rmarkdown.preview.autoRefresh", + "group": "navigation@5" + }, + { + "command": "r.browser.refresh", + "when": "resourceScheme =~ /webview/ && r.browser.active", + "group": "navigation" + }, + { + "command": "r.browser.openExternal", + "when": "resourceScheme =~ /webview/ && r.browser.active", + "group": "navigation" + }, + { + "command": "editor.action.webvieweditor.showFind", + "when": "resourceScheme =~ /webview/ && r.browser.active", + "group": "navigation" + }, + { + "command": "editor.action.webvieweditor.showFind", + "when": "resourceScheme =~ /webview/ && r.helpPanel.active", + "group": "navigation" + }, + { + "command": "r.helpPanel.back", + "when": "resourceScheme =~ /webview/ && r.helpPanel.active", + "group": "navigation" + }, + { + "command": "r.helpPanel.forward", + "when": "resourceScheme =~ /webview/ && r.helpPanel.active", + "group": "navigation" + }, + { + "command": "r.helpPanel.openExternal", + "when": "resourceScheme =~ /webview/ && r.helpPanel.active", + "group": "navigation" + }, + { + "group": "navigation", + "when": "resourceScheme =~ /webview/ && r.plot.active", + "command": "r.plot.toggleStyle" + }, + { + "group": "navigation", + "when": "resourceScheme =~ /webview/ && r.plot.active", + "command": "r.plot.toggleFullWindow" + }, + { + "group": "httpgd", + "when": "resourceScheme =~ /webview/ && r.plot.active", + "command": "r.plot.togglePreviewPlots" + }, + { + "group": "httpgd", + "when": "resourceScheme =~ /webview/ && r.plot.active", + "command": "r.plot.exportPlot" + }, + { + "group": "navigation@01", + "when": "resourceScheme =~ /webview/ && r.plot.active", + "command": "r.plot.prevPlot", + "alt": "r.plot.firstPlot" + }, + { + "group": "navigation@02", + "when": "resourceScheme =~ /webview/ && r.plot.active", + "command": "r.plot.nextPlot", + "alt": "r.plot.lastPlot" + }, + { + "group": "navigation@03", + "when": "resourceScheme =~ /webview/ && r.plot.active", + "command": "r.plot.hidePlot", + "alt": "r.plot.closePlot" + }, + { + "group": "navigation@04", + "when": "resourceScheme =~ /webview/ && r.plot.active", + "command": "r.plot.resetPlots" + }, + { + "group": "navigation", + "when": "resourceScheme =~ /webview/ && r.plot.active", + "command": "r.plot.zoomIn", + "alt": "r.plot.zoomOut" + }, + { + "group": "navigation", + "when": "resourceScheme =~ /webview/ && r.plot.active", + "command": "r.plot.zoomOut", + "alt": "r.plot.zoomIn" + }, + { + "group": "navigation", + "when": "resourceScheme =~ /webview/ && r.plot.active", + "command": "r.plot.openExternal" + }, + { + "group": "httpgd", + "when": "resourceScheme =~ /webview/ && r.plot.active", + "command": "r.plot.openUrl" + }, + { + "command": "r.rmarkdown.showPreviewToSide", + "alt": "r.rmarkdown.showPreview", + "when": "editorLangId == rmd && editorFocus", + "group": "navigation" + }, + { + "submenu": "r.knitCommands", + "when": "editorLangId == rmd && editorFocus", + "group": "@1" + }, + { + "command": "r.rmarkdown.setKnitDirectory", + "when": "editorLangId == rmd && editorFocus", + "group": "@1" + } + ], + "editor/context": [ + { + "command": "r.helpPanel.openForSelection", + "when": "resourceLangId == r" + }, + { + "command": "r.helpPanel.openForSelection", + "when": "resourceLangId == rmd" + } + ], + "view/item/context": [ + { + "command": "r.workspaceViewer.view", + "group": "inline", + "when": "view == workspaceViewer && viewItem == rootNode" + }, + { + "command": "r.workspaceViewer.remove", + "group": "inline", + "when": "view == workspaceViewer && viewItem == rootNode" + }, + { + "command": "r.helpPanel.searchPackage", + "group": "inline", + "when": "view == rHelpPages && viewItem =~ /_searchPackage_/" + }, + { + "command": "r.helpPanel.openInNewPanel", + "group": "inline", + "when": "view == rHelpPages && viewItem =~ /_openInNewPanel_/" + }, + { + "command": "r.helpPanel.clearCache", + "group": "inline", + "when": "view == rHelpPages && viewItem =~ /_clearCache_/" + }, + { + "command": "r.helpPanel.addToFavorites", + "group": "inline@9", + "when": "view == rHelpPages && viewItem =~ /_addToFavorites_/" + }, + { + "command": "r.helpPanel.removeFromFavorites", + "group": "inline@9", + "when": "view == rHelpPages && viewItem =~ /_removeFromFavorites_/" + }, + { + "command": "r.helpPanel.showOnlyFavorites", + "group": "inline@9", + "when": "view == rHelpPages && viewItem =~ /_showOnlyFavorites_/" + }, + { + "command": "r.helpPanel.showAllPackages", + "group": "inline@9", + "when": "view == rHelpPages && viewItem =~ /_showAllPackages_/" + }, + { + "command": "r.helpPanel.removePackage", + "group": "inline", + "when": "view == rHelpPages && viewItem =~ /_removePackage_/" + }, + { + "command": "r.helpPanel.updateInstalledPackages", + "group": "inline", + "when": "view == rHelpPages && viewItem =~ /_updateInstalledPackages_/" + }, + { + "command": "r.helpPanel.updatePackage", + "group": "inline", + "when": "view == rHelpPages && viewItem =~ /_updatePackage_/" + }, + { + "command": "r.helpPanel.installPackages", + "group": "inline@9", + "when": "view == rHelpPages && viewItem =~ /_installPackages_/" + }, + { + "command": "r.helpPanel.filterPackages", + "group": "inline", + "when": "view == rHelpPages && viewItem =~ /_filterPackages_/" + }, + { + "command": "r.helpPanel.showQuickPick", + "group": "inline", + "when": "view == rHelpPages && viewItem =~ /_QUICKPICK_/" + }, + { + "command": "r.helpPanel.unsummarizeTopics", + "group": "inline@8", + "when": "view == rHelpPages && viewItem =~ /_unsummarizeTopics_/" + }, + { + "command": "r.helpPanel.summarizeTopics", + "group": "inline@8", + "when": "view == rHelpPages && viewItem =~ /_summarizeTopics_/" + }, + { + "command": "r.liveShare.toggle", + "when": "view == rLiveShare" + } + ], + "view/title": [ + { + "command": "r.workspaceViewer.load", + "group": "navigation@0", + "when": "r.WorkspaceViewer:show && view == workspaceViewer && !r.liveShare:isGuest" + }, + { + "command": "r.workspaceViewer.save", + "group": "navigation@1", + "when": "r.WorkspaceViewer:show && view == workspaceViewer && !r.liveShare:isGuest" + }, + { + "command": "r.workspaceViewer.clear", + "group": "navigation@2", + "when": "r.WorkspaceViewer:show && view == workspaceViewer" + }, + { + "command": "r.workspaceViewer.refreshEntry", + "group": "navigation@3", + "when": "r.WorkspaceViewer:show && view == workspaceViewer" + }, + { + "command": "r.helpPanel.showQuickPick", + "group": "navigation", + "when": "view == rHelpPages" + }, + { + "command": "r.liveShare.retry", + "group": "navigation@3", + "when": "view == rLiveShare && r.liveShare:aborted" + } + ], + "explorer/context": [ + { + "command": "r.rmarkdown.showPreview", + "when": "resourceLangId == rmd", + "group": "navigation" + } + ], + "r.knitCommands": [ + { + "command": "r.knitRmd" + }, + { + "command": "r.knitRmdToPdf" + }, + { + "command": "r.knitRmdToHtml" + }, + { + "command": "r.knitRmdToAll" + } + ], + "file/newFile": [ + { + "group": "R", + "command": "r.rmarkdown.newFileDraft" + }, + { + "group": "R", + "command": "r.newFileDocument" + } ] }, + "submenus": [ + { + "id": "r.knitCommands", + "label": "R: Knit", + "icon": "$(zap)" + } + ], "configuration": { "type": "object", - "title": "r", + "title": "R", "properties": { + "r.rpath.windows": { + "type": "string", + "default": "", + "markdownDescription": "Path to an R executable to launch R background processes (Windows). Must be \"vanilla\" R, not radian etc.! Some variables defined in such as `${userHome}`, `${workspaceFolder}`, `${fileWorkspaceFolder}`, and `${fileDirname}` are supported." + }, + "r.rpath.mac": { + "type": "string", + "default": "", + "markdownDescription": "Path to an R executable to launch R background processes (macOS). Must be \"vanilla\" R, not radian etc.! Some variables defined in such as `${userHome}`, `${workspaceFolder}`, `${fileWorkspaceFolder}`, and `${fileDirname}` are supported." + }, + "r.rpath.linux": { + "type": "string", + "default": "", + "markdownDescription": "Path to an R executable to launch R background processes (Linux). Must be \"vanilla\" R, not radian etc.! Some variables defined in such as `${userHome}`, `${workspaceFolder}`, `${fileWorkspaceFolder}`, and `${fileDirname}` are supported." + }, "r.rterm.windows": { "type": "string", - "default": "C:\\Program Files\\R\\R-3.5.0\\bin\\x64\\R.exe", - "description": "R.exe path for windows" + "default": "", + "markdownDescription": "R path for interactive terminals (Windows). Can also be radian etc. Some variables defined in such as `${userHome}`, `${workspaceFolder}`, `${fileWorkspaceFolder}`, and `${fileDirname}` are supported." }, "r.rterm.mac": { "type": "string", - "default": "/usr/local/bin/R", - "description": "R path for Mac OS X" + "default": "", + "markdownDescription": "R path for interactive terminals (macOS). Can also be radian etc. Some variables defined in such as `${userHome}`, `${workspaceFolder}`, `${fileWorkspaceFolder}`, and `${fileDirname}` are supported." }, "r.rterm.linux": { "type": "string", - "default": "/usr/bin/R", - "description": "R path for Linux" + "default": "", + "markdownDescription": "R path for interactive terminals (Linux). Can also be radian etc. Some variables defined in such as `${userHome}`, `${workspaceFolder}`, `${fileWorkspaceFolder}`, and `${fileDirname}` are supported." }, "r.rterm.option": { "type": "array", "default": [ "--no-save", - "--no-restore", - "--no-site-file" + "--no-restore" ], - "description": "R command line options", + "description": "R command line options.", "items": { "type": "string" } }, + "r.libPaths": { + "type": "array", + "items": { + "type": "string" + }, + "default": [], + "markdownDescription": "Additional library paths to launch R background processes (R languageserver, help server, etc.). These paths will be appended to `.libPaths()` on process startup. It could be useful for projects with [renv](https://rstudio.github.io/renv/index.html) enabled." + }, + "r.useRenvLibPath": { + "type": "boolean", + "default": false, + "markdownDescription": "Use renv library paths to launch R background processes (R languageserver, help server, etc.)." + }, + "r.lsp.enabled": { + "type": "boolean", + "default": true, + "description": "Enable the R language service to provide code analysis features (completion, signature, hover, diagnostics, definition, etc.)" + }, + "r.lsp.args": { + "type": "array", + "items": { + "type": "string" + }, + "default": [], + "description": "The command line arguments to use when launching R Language Server." + }, + "r.lsp.promptToInstall": { + "type": "boolean", + "default": true, + "description": "Prompt to install R Language Server if it is not installed." + }, + "r.lsp.debug": { + "type": "boolean", + "default": false, + "description": "Debug R Language Server." + }, + "r.lsp.diagnostics": { + "type": "boolean", + "default": true, + "description": "Enable diagnostics." + }, + "r.lsp.lang": { + "type": "string", + "default": "", + "markdownDescription": "Override default `LANG` environment variable." + }, + "r.lsp.use_stdio": { + "type": "boolean", + "default": false, + "description": "Use STDIO connection instead of TCP. (Unix/macOS users only)" + }, + "r.lsp.multiServer": { + "type": "boolean", + "default": false, + "markdownDescription": "Use multiple language servers for [multi-root workspaces](https://code.visualstudio.com/docs/editor/multi-root-workspaces). Disabling this is recommended for better performance. `languageserver` > 0.3.17 supports multi-root workspace in a single server by default." + }, + "r.rmarkdown.codeLensCommands": { + "type": "array", + "items": { + "type": "string", + "enum": [ + "r.selectCurrentChunk", + "r.runCurrentChunk", + "r.runCurrentChunkAndMove", + "r.runAboveChunks", + "r.runCurrentAndBelowChunks", + "r.runBelowChunks", + "r.runAllChunks", + "r.runPreviousChunk", + "r.runNextChunk", + "r.goToPreviousChunk", + "r.goToNextChunk" + ] + }, + "default": [ + "r.runCurrentChunk", + "r.runAboveChunks" + ], + "description": "Customize RMarkdown CodeLens, which are inline commands/buttons e.g. 'Run Chunk' shown on the first line of each code chunk. \nCustomize both the commands AND its orders (that is, CodeLens respect user-specified orders):" + }, + "r.rmarkdown.enableCodeLens": { + "type": "boolean", + "default": true, + "markdownDescription": "Enable RMarkdown CodeLens, which are inline commands/buttons e.g. 'Run Chunk | Run Above' shown on the first line of each code chunk.\n\n- Click the buttons to run commands.\n- Hover on the buttons to show tooltips.\n- CodeLens commands are customizable via `#r.rmarkdown.codeLensCommands#` or settings.json `r.rmarkdown.codeLensCommands`" + }, + "r.rmarkdown.chunkBackgroundColor": { + "type": "string", + "default": "rgba(128, 128, 128, 0.1)", + "description": "RMarkdown chunk background color in RGBA or RGB value. Defaults to rgba(128, 128, 128, 0.1). Leave it empty to disable it (use default editor background color). Reload VS Code after changing settings.\n\nLearn how to set colors: https://www.w3schools.com/css/css_colors_rgb.asp.\n\nExamples for syntax rgba(, , , ):\nrgba(128, 128, 128, 0.1)\nrgba(128, 128, 128, 0.3)\nrgba(255, 165, 0, 0.1)\n\n" + }, + "r.rmarkdown.preview.autoRefresh": { + "type": "boolean", + "default": true, + "markdownDescription": "Enable automatic refresh of R Markdown preview on file update." + }, + "r.rmarkdown.preview.zoom": { + "type": "number", + "default": 1, + "markdownDescription": "Controls the zoom of the R Markdown preview." + }, + "r.rmarkdown.knit.useBackgroundProcess": { + "type": "boolean", + "default": true, + "markdownDescription": "Should knitting occur in a background process (*smart knitting*), or should it be done in the current R terminal (*manual knitting*)? \n\n*Smart knitting* includes additional features, such as custom knit function detection, R Markdown site detection, progress bars, and the setting knit directory." + }, + "r.rmarkdown.knit.focusOutputChannel": { + "type": "boolean", + "default": true, + "markdownDescription": "Should the R Markdown output channel be focused when knitting?\n\nRequires `#r.rmarkdown.knit.useBackgroundProcess#` to be set to `true`." + }, + "r.rmarkdown.knit.openOutputFile": { + "type": "boolean", + "default": false, + "markdownDescription": "Should the output file be opened automatically when using knit?\n\nRequires `#r.rmarkdown.knit.useBackgroundProcess#` to be set to `true`." + }, + "r.rmarkdown.knit.command": { + "type": "string", + "default": "rmarkdown::render", + "markdownDescription": "Command used to knit a Rmd file if not specified by the frontmatter." + }, + "r.rmarkdown.knit.defaults.knitWorkingDirectory": { + "type": "string", + "default": "document directory", + "enum": [ + "document directory", + "workspace root" + ], + "enumDescriptions": [ + "Use the document's directory as the knit directory", + "Use the workspace root as the knit directory" + ], + "markdownDescription": "What working directory should R Markdown chunks be evaluated in? Default knit behaviour is to use the document's directory as root.\n\nRequires `#r.rmarkdown.knit.useBackgroundProcess#` to be set to `true`.", + "additionalItems": false, + "additionalProperties": false + }, + "r.helpPanel.enableSyntaxHighlighting": { + "type": "boolean", + "default": true, + "description": "Enable syntax highlighting in the help panel." + }, + "r.helpPanel.cacheIndexFiles": { + "type": "string", + "enum": [ + "None", + "Workspace", + "Global" + ], + "description": "Whether/where to store parsed help indices between sessions.", + "enumDescriptions": [ + "Do not store anything", + "Store on a per workspace basis", + "Store globally" + ], + "default": "None" + }, + "r.helpPanel.previewLocalPackages": { + "type": "array", + "items": { + "type": "string" + }, + "default": [ + "." + ], + "markdownDescription": "Which local directories to try for local help pages previewer. Set to `[]` to disable." + }, + "r.helpPanel.rpath": { + "type": "string", + "default": "", + "markdownDescription": "DEPRECATED! Path to an R executable. Must be \"vanilla\" R, not radian etc.! Will be read from registry or path if not set.", + "markdownDeprecationMessage": "Will be deprecated. Use `#r.rpath.windows#`, `#r.rpath.mac#`, or `#r.rpath.linux#` instead.", + "deprecationMessage": "Will be deprecated. Use r.rpath.windows, r.rpath.mac, or r.rpath.linux instead." + }, + "r.helpPanel.enableHoverLinks": { + "type": "boolean", + "default": true, + "markdownDescription": "Show links to matching help pages in hover" + }, + "r.helpPanel.clickCodeExamples": { + "type": "object", + "markdownDescription": "What happens when clicking code examples on help pages. Might require restarting to take effect.", + "default": { + "Click": "Copy", + "Ctrl+Click": "Run", + "Shift+Click": "Ignore" + }, + "properties": { + "Click": { + "type": "string", + "default": "Copy", + "enum": [ + "Ignore", + "Copy", + "Run" + ], + "enumDescriptions": [ + "Do nothing", + "Copy the code to the clipboard", + "Run the code in the terminal" + ] + }, + "Ctrl+Click": { + "type": "string", + "default": "Run", + "enum": [ + "Ignore", + "Copy", + "Run" + ], + "enumDescriptions": [ + "Do nothing", + "Copy the code to the clipboard", + "Run the code in the terminal" + ] + }, + "Shift+Click": { + "type": "string", + "default": "Ignore", + "enum": [ + "Ignore", + "Copy", + "Run" + ], + "enumDescriptions": [ + "Do nothing", + "Copy the code to the clipboard", + "Run the code in the terminal" + ] + } + }, + "additionalProperties": false + }, "r.source.encoding": { "type": "string", "default": "UTF-8", - "description": "An optional encoding to pass to R when executing the file, i.e. 'source(FILE, encoding=ENCODING)'" + "markdownDescription": "An optional encoding to pass to R when executing the file, i.e. `source(FILE, encoding=ENCODING)`." + }, + "r.source.echo": { + "type": "boolean", + "default": false, + "markdownDescription": "Should the file be executed with echo option set to TRUE by default, i.e. `source(FILE, echo=TRUE)`?" }, "r.source.focus": { "type": "string", "default": "editor", "enum": [ "editor", - "terminal" + "terminal", + "none" + ], + "enumDescriptions": [ + "Focus editor when sending code to terminal", + "Focus terminal when sending code to terminal", + "Do not show terminal when sending code to terminal" ], - "description": "Keeping focus when running" + "description": "What to show/focus after sending code to terminal." }, "r.alwaysUseActiveTerminal": { "type": "boolean", "default": false, - "description": "Use active terminal for all commands, rather than creating a new R terminal" + "description": "Use active terminal for all commands, rather than creating a new R terminal." }, "r.bracketedPaste": { "type": "boolean", "default": false, - "description": "Use bracketed paste mode when sending code to console. Enable for Radian console" + "markdownDescription": "Use bracketed paste mode when sending code to terminal. Enable for [radian](https://github.com/randy3k/radian) console." + }, + "r.removeLeadingComments": { + "type": "boolean", + "default": false, + "description": "Remove leading comments when sending code to terminal." + }, + "r.sessionWatcher": { + "type": "boolean", + "default": true, + "description": "Enable R session watcher. Required for workspace viewer and most features to work with an R session. Restart required to take effect." + }, + "r.session.useWebServer": { + "type": "boolean", + "default": false, + "markdownDescription": "Enable experimental use of web server in the R session to handle session requests from the extension. Changes the option `vsc.use_webserver` in R. Requires `#r.sessionWatcher#` to be set to `true`. Requires the `httpuv` R package." + }, + "r.session.watchGlobalEnvironment": { + "type": "boolean", + "default": true, + "markdownDescription": "Watch the global environment to provide hover, autocompletions, and workspace viewer information. Changes the option `vsc.globalenv` in R. Requires `#r.sessionWatcher#` to be set to `true`." + }, + "r.session.objectLengthLimit": { + "type": "integer", + "default": 2000, + "markdownDescription": "The upper limit of object length to show object details in workspace viewer and provide session symbol completion. Decrease this value if you experience significant delay after executing R commands caused by large global objects with many elements. Changes the option `vsc.object_length_limit` in R. Requires `#r.sessionWatcher#` to be set to `true`." + }, + "r.session.objectTimeout": { + "type": "integer", + "default": 50, + "markdownDescription": "The maximum number of milliseconds to get information of a single object in the global environment. Decrease this value if you experience significant delay after executing R commands caused by large global objects with many elements. Changes the option `vsc.object_timeout` in R. Requires `#r.sessionWatcher#` to be set to `true`." + }, + "r.session.levelOfObjectDetail": { + "type": "string", + "markdownDescription": "How much of the object to show on hover, autocompletion, and in the workspace viewer? Changes the option `vsc.str.max.level` in R. Requires `#r.sessionWatcher#` to be set to `true`.", + "default": "Minimal", + "enum": [ + "Minimal", + "Normal", + "Detailed" + ], + "enumDescriptions": [ + "Display literal values and object types only.", + "Display the top level of list content, data frame column values, and example values.", + "Display the top two levels of list content, data frame column values, and example values. This option may cause notable delay after each user input in the terminal." + ] + }, + "r.session.emulateRStudioAPI": { + "type": "boolean", + "default": true, + "markdownDescription": "Emulate the RStudio API for addin support and other {rstudioapi} calls. Changes the option `vsc.rstudioapi` in R. Requires `#r.sessionWatcher#` to be set to `true`." + }, + "r.session.data.rowLimit": { + "type": "integer", + "default": 0, + "markdownDescription": "The maximum number of rows to be displayed in the data viewer. `0` means no limit. Changes the option `vsc.row_limit` in R. Requires `#r.sessionWatcher#` to be set to `true`." + }, + "r.session.data.pageSize": { + "type": "integer", + "default": 500, + "markdownDescription": "The maximum number of rows per page in the data viewer. `0` means to disable pagination." + }, + "r.session.viewers.viewColumn": { + "type": "object", + "markdownDescription": "Which view column should R-related webviews be displayed? Requires `#r.sessionWatcher#` to be set to `true`.", + "default": { + "plot": "Two", + "browser": "Active", + "viewer": "Two", + "pageViewer": "Active", + "view": "Two", + "helpPanel": "Two" + }, + "properties": { + "plot": { + "type": "string", + "description": "Which view column to show the plot file on graphics update? \n\nChanges the option 'vsc.plot' in R.", + "enum": [ + "Two", + "Active", + "Beside", + "Disable" + ], + "enumDescriptions": [ + "Display plots in editor group 2.", + "Display plots in the active editor.", + "Display plots beside the active editor.", + "Do not use the VSCode-R plot viewer." + ], + "default": "Two" + }, + "browser": { + "type": "string", + "description": "Which view column to show the WebView triggered by browser (e.g. shiny apps)? \n\nChanges the option 'vsc.browser' in R.", + "enum": [ + "Two", + "Active", + "Beside", + "Disable" + ], + "enumDescriptions": [ + "Display browser in editor group 2.", + "Display browser in the active editor.", + "Display browser beside the active editor.", + "Do not use the VSCode-R browser." + ], + "default": "Active" + }, + "viewer": { + "type": "string", + "description": "Which view column to show the WebView triggered by viewer (e.g. htmlwidgets)? \n\nChanges the option 'vsc.viewer' in R.", + "enum": [ + "Two", + "Active", + "Beside", + "Disable" + ], + "enumDescriptions": [ + "Display viewer in editor group 2.", + "Display viewer in the active editor.", + "Display viewer beside the active editor.", + "Do not use the VSCode-R viewer." + ], + "default": "Two" + }, + "pageViewer": { + "type": "string", + "description": "Which view column to show the WebView triggered by the page viewer (e.g. profvis)? \n\nChanges the option 'vsc.page_viewer' in R.", + "enum": [ + "Two", + "Active", + "Beside", + "Disable" + ], + "enumDescriptions": [ + "Display page viewer in editor group 2.", + "Display page viewer in the active editor.", + "Display page viewer beside the active editor.", + "Do not use the VSCode-R page viewer." + ], + "default": "Active" + }, + "view": { + "type": "string", + "description": "Which view column to show the WebView triggered by View()? \n\nChanges the option 'vsc.view' in R.", + "enum": [ + "Two", + "Active", + "Beside", + "Disable" + ], + "enumDescriptions": [ + "Display view output in editor group 2.", + "Display view output in the active editor.", + "Display view output beside the active editor.", + "Do not use the VSCode-R view command." + ], + "default": "Two" + }, + "helpPanel": { + "type": "string", + "description": "Which view column to show the WebView triggered by the help panel? \n\nChanges the option 'vsc.help_panel' in R.", + "enum": [ + "Two", + "Active", + "Beside", + "Disable" + ], + "enumDescriptions": [ + "Display help panel in editor group 2.", + "Display help panel in the active editor.", + "Display help panel beside the active editor.", + "Do not use the VSCode-R help panel." + ], + "default": "Two" + } + }, + "additionalProperties": false + }, + "r.rtermSendDelay": { + "type": "integer", + "default": 8, + "markdownDescription": "Delay in milliseconds before sending each line to rterm. Requires `#r.bracketedPaste#` to be `false`." + }, + "r.workspaceViewer.showObjectSize": { + "type": "boolean", + "default": false, + "markdownDescription": "Show object size when hovering over a workspace viewer item. Changes the option `vsc.show_object_size` in R." + }, + "r.workspaceViewer.removeHiddenItems": { + "type": "boolean", + "default": false, + "description": "Remove hidden items when clearing workspace." + }, + "r.workspaceViewer.clearPrompt": { + "type": "boolean", + "default": true, + "description": "Prompt the user for confirmation when clearing the workspace." + }, + "r.liveShare.timeout": { + "type": "integer", + "default": 10000, + "description": "Time in milliseconds before aborting attempt to connect to Live Share API." + }, + "r.liveShare.defaults.commandForward": { + "type": "boolean", + "default": false, + "description": "Default boolean value for guest command forwarding." + }, + "r.liveShare.defaults.shareWorkspace": { + "type": "boolean", + "default": true, + "description": "Default boolean value for sharing the R workspace with guests." + }, + "r.liveShare.defaults.shareBrowser": { + "type": "boolean", + "default": false, + "description": "Default boolean value for automatically sharing R browser ports with guests." + }, + "r.plot.devArgs": { + "type": "object", + "markdownDescription": "The arguments for the png device to replay user graphics to show in VSCode. Requires `#r.plot.useHttpgd#` to be set to `false`. \n\nChanges the option `vsc.dev.args` in R.", + "default": { + "width": 800, + "height": 1200 + }, + "properties": { + "width": { + "type": "number", + "description": "Width of the graphic device.", + "default": 480 + }, + "height": { + "type": "number", + "description": "Height of the graphic device.", + "default": 480 + }, + "units": { + "type": "string", + "default": "px" + }, + "pointsize": { + "type": "number", + "default": 12 + }, + "bg": { + "type": "string", + "default": "white" + } + }, + "additionalProperties": false + }, + "r.plot.useHttpgd": { + "type": "boolean", + "default": false, + "markdownDescription": "Use the httpgd-based plot viewer instead of the base VSCode-R plot viewer. Changes the option `vsc.use_httpgd` in R.\n\nRequires the `httpgd` R package version 1.2.0 or later." + }, + "r.plot.defaults.colorTheme": { + "type": "string", + "default": "original", + "enum": [ + "vscode", + "original" + ], + "markdownEnumDescriptions": [ + "Match background and primary stroke color to the current color theme (Or apply custom CSS overwrites, if specified in `#r.plot.customStyleOverwrites#`)", + "Use original colors" + ], + "markdownDescription": "Which color theme to use when launching the httpgd plot viewer." + }, + "r.plot.defaults.plotPreviewLayout": { + "type": "string", + "default": "multirow", + "enum": [ + "multirow", + "scroll", + "hidden" + ], + "enumDescriptions": [ + "Show in multiple rows", + "Show scrollbar", + "Don't show preview plots" + ], + "markdownDescription": "How to display plot previews if more than one row required." + }, + "r.plot.defaults.fullWindowMode": { + "type": "boolean", + "default": false, + "markdownDescription": "Whether to use full window mode when launching the httpgd plot viewer." + }, + "r.plot.timing.resizeInterval": { + "type": "number", + "default": 100, + "markdownDescription": "Interval in ms to wait between plot resizes." + }, + "r.plot.timing.refreshInterval": { + "type": "number", + "default": 10, + "markdownDescription": "Interval in ms to wait between plot refreshs." + }, + "r.plot.customStyleOverwrites": { + "type": "string", + "default": "", + "markdownDescription": "Path to a custom CSS file to be used when `#r.plot.defaults.colorTheme#` is `vscode`. Replaces the default CSS overwrites!" + } + } + }, + "configurationDefaults": { + "[r]": { + "editor.wordSeparators": "`~!@#$%^&*()-=+[{]}\\|;:'\",<>/" + } + }, + "taskDefinitions": [ + { + "type": "R", + "required": [ + "code" + ], + "properties": { + "code": { + "type": "array", + "items": { + "type": "string" + }, + "default": [ + "x <- 'Hello, World!'", + "print(x)" + ], + "description": "R code to be executed" + }, + "options": { + "type": "array", + "items": { + "type": "string" + }, + "default": [ + "--no-echo", + "--no-restore" + ], + "description": "Command line options used to invoke R. (see R --help)" + }, + "cwd": { + "type": "string", + "default": "${workspaceRoot}", + "description": "The current working directory of the executed program or shell. If omitted, the current workspace root will be used." + }, + "env": { + "type": "object", + "additionalProperties": { + "type": "string" + }, + "default": {}, + "description": "The environment of the executed program or shell. If omitted, the current process environment will be used." + } } } - } + ], + "problemMatchers": [ + { + "name": "testthat", + "owner": "R", + "severity": "error", + "fileLocation": [ + "relative", + "${workspaceFolder}/tests/testthat" + ], + "pattern": [ + { + "regexp": "^[-─ ]*(Failure|Error)\\s\\((.*\\.[Rr]):(\\d+):?(\\d+)?\\):\\s(.*)", + "file": 2, + "line": 3, + "column": 4, + "message": 5 + }, + { + "regexp": "^(.*)$", + "message": 1 + } + ] + } + ] }, "scripts": { - "vscode:prepublish": "webpack --mode production", - "compile": "webpack --mode none", - "watch": "webpack --mode none --watch", - "test-compile": "tsc -p ./" + "vscode:prepublish": "tsc -p ./html/help && tsc -p ./html/httpgd && node esbuild.js --production", + "changelog": "npx git-cliff v2.8.5.. -o", + "build": "tsc -p ./html/help && tsc -p ./html/httpgd && node esbuild.js", + "watch": "node esbuild.js --watch", + "watchHelp": "tsc -p ./html/help --watch", + "watchHttpgd": "tsc -p ./html/httpgd --watch", + "pretest": "tsc -p ./", + "test": "node ./out/test/runTest.js", + "lint": "eslint src --ext ts" }, "devDependencies": { - "@types/fs-extra": "^8.0.0", - "@types/mocha": "^5.2.7", - "@types/node": "^12.7.8", - "mocha": "^6.2.0", - "node-atomizr": "^0.6.1", - "ts-loader": "^6.2.0", - "tslint": "^5.20.0", - "typescript": "^3.6.3", - "vscode": "^1.1.33", - "webpack": "^4.41.0", - "webpack-cli": "^3.3.9", - "yamljs": "^0.3.0" + "@types/cheerio": "^0.22.29", + "@types/ejs": "^3.0.6", + "@types/express": "^4.17.12", + "@types/fs-extra": "^9.0.11", + "@types/glob": "^8.0.0", + "@types/highlight.js": "^10.1.0", + "@types/js-yaml": "^4.0.2", + "@types/mocha": "^8.2.2", + "@types/node": "^18.17.1", + "@types/node-fetch": "^2.5.10", + "@types/sinon": "^10.0.13", + "@types/vscode": "^1.75.0", + "@types/winreg": "^1.2.31", + "@typescript-eslint/eslint-plugin": "^5.30.0", + "@typescript-eslint/parser": "^5.30.0", + "@vscode/test-electron": "^2.2.3", + "esbuild": "^0.27.3", + "eslint": "^7.28.0", + "eslint-plugin-jsdoc": "^35.1.3", + "git-cliff": "^2.12.0", + "mocha": "^11.1.0", + "sinon": "^15.0.1", + "typescript": "^4.7.2" }, "dependencies": { - "fs-extra": "^8.1.0", - "path": "^0.12.7", - "winattr": "^3.0.0" - } + "ag-grid-community": "^31.3.2", + "cheerio": "1.0.0-rc.12", + "crypto": "^1.0.1", + "ejs": "^3.1.10", + "fs-extra": "^10.0.0", + "highlight.js": "^11.9.0", + "httpgd": "0.1.6", + "jquery": "^3.7.1", + "jquery.json-viewer": "^1.5.0", + "js-yaml": "^4.1.0", + "node-fetch": "^2.6.7", + "vscode-languageclient": "^9.0.1", + "vsls": "^1.0.4753", + "winreg": "^1.2.4" + }, + "extensionDependencies": [ + "REditorSupport.r-syntax" + ] } diff --git a/snippets/markdown.json b/snippets/markdown.json deleted file mode 100644 index 6bba2591d..000000000 --- a/snippets/markdown.json +++ /dev/null @@ -1,71 +0,0 @@ -{ - "Insert bold text": { - "prefix": "bold", - "body": "**${1:${TM_SELECTED_TEXT}}**$0", - "description": "Insert bold text" - }, - "Insert italic text": { - "prefix": "italic", - "body": "*${1:${TM_SELECTED_TEXT}}*$0", - "description": "Insert italic text" - }, - "Insert quoted text": { - "prefix": "quote", - "body": "> ${1:${TM_SELECTED_TEXT}}", - "description": "Insert quoted text" - }, - "Insert code": { - "prefix": "code", - "body": "`${1:${TM_SELECTED_TEXT}}`$0", - "description": "Insert code" - }, - "Insert fenced code block": { - "prefix": "fenced codeblock", - "body": [ - "```${1:language}", - "$0", - "```" - ], - "description": "Insert fenced code block" - }, - "Insert heading": { - "prefix": "heading", - "body": "# ${1:text}", - "description": "Insert heading" - }, - "Insert unordered list": { - "prefix": "unordered list", - "body": [ - "- ${1:first}", - "- ${2:second}", - "- ${3:third}", - "$0" - ], - "description": "Insert unordered list" - }, - "Insert ordered list": { - "prefix": "ordered list", - "body": [ - "1. ${1:first}", - "2. ${2:second}", - "3. ${3:third}", - "$0" - ], - "description": "Insert ordered list" - }, - "Insert horizontal rule": { - "prefix": "horizontal rule", - "body": "----------\n", - "description": "Insert horizontal rule" - }, - "Insert link": { - "prefix": "link", - "body": "[${1:text}](http://${2:link})$0", - "description": "Insert link" - }, - "Insert image": { - "prefix": "image", - "body": "![${1:alt}](http://${2:link})$0", - "description": "Insert image" - } -} diff --git a/snippets/r-snippets.json b/snippets/r-snippets.json index 79b3db6f4..2a626833a 100644 --- a/snippets/r-snippets.json +++ b/snippets/r-snippets.json @@ -1,283 +1,284 @@ { - "rug":{ + "rug": { "prefix": "rug", "body": "rug(${1:jitter(${2:x})})", "descriptison": "Add Tick Marks" }, - "app":{ + "app": { "prefix": "apply", "body": "apply(${1:X}, ${2:MARGIN}, ${3:FUN}, ${4:...})", "description": "Apply" }, - "att":{ + "att": { "prefix": "attach", "body": "attach(${1:frame})", "description": "Attach" }, - "cat":{ + "cat": { "prefix": "cat", "body": "cat(${1:file}, ${2:sep = ${3:\"\"}}, ${4:fill = ${5:FALSE}}, ${6:labels = ${7:NULL}}, ${8:append = ${9:FALSE}})", "description": "Cat" }, - "cum":{ + "cum": { "prefix": "cum", "body": "cum${1:max}(${2:x})", "description": "Cummulative" }, - "cuma":{ + "cuma": { "prefix": "cumaax", "body": "cummax(${1:x}", "description": "Cummulative max" - }, - "cumi":{ + "cumi": { "prefix": "cumin", "body": "cummin(${1:x}", "description": "Cummulative min" }, - "daf":{ + "daf": { "prefix": "data.frame", "body": "data.frame(${1:...}, ${2:row.names = ${3:NULL}}, ${4:check.rows = ${5:FALSE}}, ${6:check.names = ${7:TRUE}}, ${8:stringsAsFactors = ${9:default.stringsAsFactors()}})", "description": "Data Frame" }, - "den":{ + "den": { "prefix": "density", "body": "density(${1:x}${2:, bw = ${3:bandwidth}})", "description": "Density" }, - "det":{ + "det": { "prefix": "detach", "body": "detach(${0:})", "description": "Detach" }, - "cut":{ + "cut": { "prefix": "cut", "body": "cut(${1:x}, breaks = c(${2:${3:}, ${4:max(${1:x})}}))", "description": "Divide Into Intervals" }, - "exp":{ + "exp": { "prefix": "exppand", "body": "expand.grid(${1:...}, ${2:KEEP.OUT.ATTRS = ${3:TRUE}}, ${4:stringsAsFactors = ${5:TRUE}})", "description": "Expand grid" }, - "fac":{ + "fac": { "prefix": "factor", "body": "factor(${1:x})", "description": "Factor" }, - "fun":{ + "fun": { "prefix": "function", "body": "function(${1:x}) ${3:{$0\\}}", "description": "Function" }, - "glm":{ + "glm": { "prefix": "glm", "body": "glm(${1:formula}, ${2:family = {3:gaussian}}, ${4:data})", "description": "Generalized Linear Models" }, - "grep":{ + "grep": { "prefix": "grep", "body": "grep(${1:pattern}, ${2:x}, ${3:ignore.case = ${4:FALSE}}, ${5:perl = ${6:FALSE}})", "description": "Grep" }, - "grep2":{ + "grep2": { "prefix": "grep", "body": "grep(${1:pattern}, ${2:x}, ${3:ignore.case = ${4:FALSE}}, ${5:perl = ${6:FALSE}}, ${7:value = ${8:FALSE}}, ${9:fixed = ${10:TRUE}})", "description": "Grep" }, - "grepl":{ + "grepl": { "prefix": "grepl", "body": "grep(${1:pattern}, ${2:x}, ${3:ignore.case = ${4:FALSE}}, ${5:perl = ${6:FALSE}}, ${7:fixed = ${8:TRUE}})", "description": "Grep logical" }, - "ins":{ + "ins": { "prefix": "insall", "body": "install.packages(${1:pkgs}, ${2:lib}, ${3:repos = ${4:getOption(\"repos\")}}, ${5:contriburl = ${6:contrib.url(repos, type)}})", "description": "Install package" }, - "km":{ + "km": { "prefix": "kmeans", "body": "kmeans(${1:x}, ${2:centers}, ${3:iter.max = ${4:10}}, ${5:nstart = ${6:1}}, ${7:algorithm = ${8:c(\"Hartigan-Wong\", \"Lloyd\", \"Forgy\",\"MacQueen\")}}, ${9:trace = ${10:FALSE}})", "description": "Kmeans" }, - "kr":{ + "kr": { "prefix": "kruskal", "body": "kruskal.test(${1:formula}, ${2:data}, ${3:subset}, ${4:na.action}, ${5:...})", "description": "Kruskal-Wallis Rank Sum test" }, - "lap":{ + "lap": { "prefix": "lapply", "body": "lapply(${1:X}, ${2:FUN}, ${3:...})", "description": "Lapply" }, - "len":{ + "len": { "prefix": "length", "body": "length(${1:x})", "description": "Length" }, - "lib":{ + "lib": { "prefix": "library", "body": "library(${1:package}, ${2:help}, ${3:pos = ${4:2}}, ${5:lib.loc = ${6:NULL}})", "description": "Library" }, - "lin":{ + "lin": { "prefix": "lines", "body": "lines(${1:x}${2:, color=${3:red}})", "description": "Polygonal Line" }, - "dat":{ + "dat": { "prefix": "data", "body": "data(${1:name})", "description": "Load Dataset" }, - "loa":{ + "loa": { "prefix": "load", "body": "load(${1:file}, ${2:envir = ${3:parent.frame()}}, ${4:verbose = ${5:FALSE}})", "description": "Load RData" }, - "mea":{ + "mea": { "prefix": "mean", "body": "mean(${1:x}${2:, na.rm=${3:FALSE}})", "description": "Mean" }, - "mer":{ + "mer": { "prefix": "merge", "body": "merge(${1:x}, ${2:y}, ${3:by = ${4:intersect(names(x), names(y))}}, ${5:all = ${6:FALSE}})", "description": "Merge" }, - "nam":{ + "nam": { "prefix": "names", "body": "names(${1:x})", "description": "Names" }, - "opt":{ + "opt": { "prefix": "option", "body": "options(${1:...})", "description": "Options" }, - "ord":{ + "ord": { "prefix": "order", "body": "order(${1:...}, ${2:na.last = ${3:TRUE}}, ${4:decreasing = ${5:FALSE}})", "description": "Order" }, - "out":{ + "out": { "prefix": "outer", "body": "outer(${1:X}, ${2:Y}, ${3:FUN = ${4:\"*\"}}, ${5:...})", "description": "Outer" }, - "pas":{ + "pas": { "prefix": "paste", "body": "paste(${1:...}, ${2:sep = ${3:\" \"}}, ${3:collapse = ${4:NULL}})", "description": "Paste" }, - "pas0":{ + "pas0": { "prefix": "pas0", "body": "paste(${1:...}, ${2:collapse = ${3:NULL}})", "description": "Paste0" }, - "plot":{ + "plot": { "prefix": "plot", "body": "plot(${1:x}, ${2:y}, ${3:...})", "description": "Plot" }, - "poi":{ + "poi": { "prefix": "point", "body": "points(${1:x}, ${2:y = ${3:NULL}}, ${4:type = ${5:\"p\"}}, ${6:...})", "description": "Points" }, - "pri":{ + "pri": { "prefix": "print", "body": "print(${1:x}, ${2:...})", "description": "Print" }, - "qua":{ + "qua": { "prefix": "quantile", "body": "quantile(${1:x}, ${2:probs = ${3:seq(0, 1, 0.25)}}, ${4:na.rm = ${5:FALSE}}, ${6:names = ${7:TRUE}}, ${8:type = ${9:7}}, ${10:...})", "description": "Quantile" }, - "reat":{ + "reat": { "prefix": "read", "body": "read.table('${1:filename}'${2:, header = ${3:TRUE}, sep = '${4:\t}', stringsAsFactors = ${5:FALSE}})", "description": "Read From File" }, - "rep":{ + "rep": { "prefix": "rep", "body": "rep(${1:x}, ${2:...})", "description": "Rep" }, - "sam":{ + "sam": { "prefix": "sample", "body": "sample(${1:x}, ${2:size}, ${3:replace = ${4:FALSE}}, ${5:prob = ${6:NULL}})", "description": "Sample" }, - "seq":{ + "seq": { "prefix": "seq", "body": "seq(${1:from}, ${2:to}, ${3:by})", "description": "Sequence (from,to,by)" }, - "sor":{ + "sor": { "prefix": "sort", "body": "sort(${1:x})", "description": "Sort" }, - "sou":{ + "sou": { "prefix": "source", "body": "source(${1:'${2:}'}${3:, chdir = ${4:TRUE}})", "description": "Source" }, - "sd":{ + "sd": { "prefix": "sd", "body": "sd(${1:x}${2:, na.rm=${3:FALSE}})", "description": "Standard deviation" }, - "tab":{ + "tab": { "prefix": "table", "body": "table(${1:...})", "description": "Table" }, - "tap":{ + "tap": { "prefix": "tapply", "body": "tapply(${1:X}, ${2:INDEX}, ${3:FUN = ${4:NULL}}, ${5:...}, ${6:simplify = ${7:TRUE}})", "description": "Tapply" }, - "uni":{ + "uni": { "prefix": "unique", "body": "unique(${1:x}, ${2:incomparables = ${3:FALSE}}, ${4:...})", "description": "Unique" }, - "whi":{ + "whi": { "prefix": "which", "body": "which(${1:x}, ${2:arr.ind = ${3:FALSE}}, ${4:useNames = ${5:TRUE}})", "description": "Which" }, - "wric":{ + "wric": { "prefix": "write", "body": "write.csv(${1:x}, ${2:file = ${3:\"\"}}, ${4:append = ${5:FALSE}}, ${6:quote = ${7:TRUE}}, ${8:sep = ${9:\" \"}}", "description": "Write csv" }, - "fch":{ + "fch": { "prefix": "fch", "body": "${1:file = }file.choose()${0:}", "description": "file.choose" }, "Clustering": { "prefix": "clara", - "body":[ + "body": [ "library(cluster)", "clara(${1:xData}, k = ${2:k}, metric = ${3:metric}, stand = ${4:stand}, samples = ${5:samples})" ], - "description":"Cluster data into k clusters (library: cluster)" + "description": "Cluster data into k clusters (library: cluster)" }, - "Linear model":{ + "Linear model": { "prefix": "lm", - "body": ["lmObj <- lm(${1:yCol} ~ {2:xCol}, data = ${3:data}, subset = ${4:subset}, weights = ${5:weights}, na.action = ${6:na.action})", + "body": [ + "lmObj <- lm(${1:yCol} ~ {2:xCol}, data = ${3:data}, subset = ${4:subset}, weights = ${5:weights}, na.action = ${6:na.action})", "summary(lmObj)" ], "description": "Fit a linear model" }, - "Linear model with plot":{ + "Linear model with plot": { "prefix": "lmplot", - "body": ["lmObj <- lm(${1:yCol} ~ {2:xCol}, data = ${3:data}, subset = ${4:subset}, weights = ${5:weights}, na.action = ${6:na.action})", + "body": [ + "lmObj <- lm(${1:yCol} ~ {2:xCol}, data = ${3:data}, subset = ${4:subset}, weights = ${5:weights}, na.action = ${6:na.action})", "summary(lmObj)", "plot(lmObj, which=c(1)) # Plot residuals versus fitted" ], @@ -285,9 +286,10 @@ }, "loess": { "prefix": "loess", - "body": ["loess(${1:yCol} ~ {2:xCol}, data = ${3:data}, subset = ${4:subset}, weights = ${5:weights}, span = ${6:span},", + "body": [ + "loess(${1:yCol} ~ {2:xCol}, data = ${3:data}, subset = ${4:subset}, weights = ${5:weights}, span = ${6:span},", "degree = ${7:degree}, na.action = {8:na.action})" - ], + ], "description": "Local polynomial regression" }, "Descriptive statistics summary": { @@ -324,29 +326,29 @@ "body": "${1:outFactor} <- factor(x = ${2:dataVec}, levels = ${3:levels}, exclude = ${4:exclude})", "description": "Create a factor (categorical variable) from a vector" }, - "Matrix":{ + "Matrix": { "prefix": "matrix", "body": "${1:outMatrix} <- matrix(data = ${2:dataVec}, nrow = ${3:nrow}, ncol = ${4:ncol}, byrow = ${5:byrow})", "description": "Create a matrix from a vector" }, - "remove missings":{ + "remove missings": { "prefix": "naomit", "body": "${1:newdataframe} <- na.omit(${2:dataframe})", "description": "Remove all rows with missing values from data frame" }, - "Read CSV":{ + "Read CSV": { "prefix": "readc", - "body": "${1:dfname} <- read.csv(file = \"${2:file}\", header = ${3|TRUE,FALSE|}, sep = \"${4:\\,}\", row.names = ${5:NULL}, stringsAsFactors = ${6|FALSE,TRUE|}})", + "body": "${1:dfname} <- read.csv(file = \"${2:file}\", header = ${3|TRUE,FALSE|}, sep = \"${4|\\,,;|}\", row.names = ${5:NULL}, stringsAsFactors = ${6|FALSE,TRUE|})", "description": "Read a data table from a comma-separated file (CSV) and create a data frame" }, "else": { "prefix": "else", "body": [ "else {", - " ${1:selected}", - "}" + " ${1:selected}", + "}" ], - "description": "Code snippet for 'else' conditional" + "description": "Code snippet for 'else' conditional" }, "elseif": { "prefix": "elseif", @@ -355,9 +357,9 @@ " ${2:selected}", "}" ], - "description": "Code snippet for 'else' conditional" + "description": "Code snippet for 'else' conditional" }, - "for":{ + "for": { "prefix": "for", "body": [ "for (${1:identifier} in ${2:collection}) {", @@ -369,11 +371,11 @@ "function": { "prefix": "function", "body": [ - "${1:name} <- function(${2:parameters})", + "${1:name} <- function(${2:parameters}) {", " ${3:selected}", "}" ], - "description": "Code snippet for 'if' conditional" + "description": "Named function" }, "if": { "prefix": "if", @@ -382,7 +384,7 @@ " ${2:selected}", "}" ], - "description": "Code snippet for 'if' conditional" + "description": "Code snippet for 'if' conditional" }, "if-else block": { "prefix": "ifelse", @@ -393,20 +395,81 @@ " ", "}" ], - "description": "Code snippet for 'if-else' conditional block" - }, - "Region Start": { - "prefix": "#region", - "body": [ - "#region $0" - ], - "description": "Folding Region Start" - }, - "Region End": { - "prefix": "#endregion", - "body": [ - "#endregion" - ], - "description": "Folding Region End" - } + "description": "Code snippet for 'if-else' conditional block" + }, + "Region Start": { + "prefix": "#region", + "body": [ + "#region $0" + ], + "description": "Folding Region Start" + }, + "Region End": { + "prefix": "#endregion", + "body": [ + "#endregion" + ], + "description": "Folding Region End" + }, + "Shiny sidebar page": { + "prefix": "shiny sidebar page", + "body": [ + "library(shiny)", + "", + "ui <- fluidPage(", + " titlePanel(\"${1:title}\"),", + " sidebarLayout(", + " sidebarPanel(),", + " mainPanel()", + " )", + ")", + "", + "server <- function(input, output, session) {", + "", + "}", + "", + "shinyApp(ui, server)$0" + ], + "description": "Shiny sidebar page" + }, + "Shiny observe event": { + "prefix": "shiny observe event", + "body": [ + "observeEvent(${1:event}, {", + " ${0}", + "})" + ], + "description": "Shiny observe event" + }, + "Shiny withProgress": { + "prefix": "shiny withProgress", + "body": [ + "withProgress(message = \"${1:message}\", {", + "${0}", + "})" + ], + "description": "Shiny withProgress" + }, + "Shiny module": { + "prefix": "shiny module", + "body": [ + "${1:name}ui <- function(id) {", + " ns <- NS(id)", + " tagList(", + " ${0}", + " )", + " }", + "", + "${1:name} <- function(input, output, session) {", + " ns <- session\\$ns", + "}", + "", + "# Copy in UI", + "#${1:name}ui(\"${1:name}ui\")", + "", + "# Copy in server", + "#callModule(${1:name}, \"${1:name}ui\")" + ], + "description": "Shiny module" + } } \ No newline at end of file diff --git a/snippets/rmarkdown.json b/snippets/rmarkdown.json new file mode 100644 index 000000000..f44ca7bd4 --- /dev/null +++ b/snippets/rmarkdown.json @@ -0,0 +1,459 @@ +{ + "Insert bold text": { + "prefix": "bold", + "body": "**${1:${TM_SELECTED_TEXT}}**$0", + "description": "Insert bold text" + }, + "Insert italic text": { + "prefix": "italic", + "body": "*${1:${TM_SELECTED_TEXT}}*$0", + "description": "Insert italic text" + }, + "Insert superscript": { + "prefix": "superscript", + "body": "^${1:${TM_SELECTED_TEXT}}^$0", + "description": "Insert superscript" + }, + "Insert subscript": { + "prefix": "subscript", + "body": "~${1:${TM_SELECTED_TEXT}}~$0", + "description": "Insert subscript" + }, + "Insert strikeout": { + "prefix": "strikeout", + "body": "~~${1:${TM_SELECTED_TEXT}}~~$0", + "description": "Insert strikeout" + }, + "Insert quoted text": { + "prefix": "quote", + "body": "> ${1:${TM_SELECTED_TEXT}}", + "description": "Insert quoted text" + }, + "Insert code": { + "prefix": "code", + "body": "`${1:${TM_SELECTED_TEXT}}`$0", + "description": "Insert code" + }, + "Insert fenced code block": { + "prefix": "fenced codeblock", + "body": [ + "```${1:language}", + "$0", + "```" + ], + "description": "Insert fenced code block" + }, + "Insert heading": { + "prefix": "heading", + "body": "# ${1:text}", + "description": "Insert heading" + }, + "Insert unordered list": { + "prefix": "unordered list", + "body": [ + "- ${1:first}", + "- ${2:second}", + "- ${3:third}", + "$0" + ], + "description": "Insert unordered list" + }, + "Insert ordered list": { + "prefix": "ordered list", + "body": [ + "1. ${1:first}", + "2. ${2:second}", + "3. ${3:third}", + "$0" + ], + "description": "Insert ordered list" + }, + "Insert unordered item": { + "prefix": "unordered item", + "body": "- ${1:${TM_SELECTED_TEXT}}", + "description": "Insert unordered item" + }, + "Insert ordered item": { + "prefix": "ordered item", + "body": "1. ${1:${TM_SELECTED_TEXT}}", + "description": "Insert ordered item" + }, + "Insert task list": { + "prefix": "task list", + "body": [ + "- [${1|x, |}] ${2:first}", + "- [${3|x, |}] ${4:second}", + "- [${5|x, |}] ${6:third}", + "$0" + ], + "description": "Insert task list" + }, + "Insert checked item": { + "prefix": "checked item", + "body": "- [x] ${1:${TM_SELECTED_TEXT}}", + "description": "Insert checked item" + }, + "Insert unchecked item": { + "prefix": "unchecked item", + "body": "- [ ] ${1:${TM_SELECTED_TEXT}}", + "description": "Insert unchecked item" + }, + "Insert horizontal rule": { + "prefix": "horizontal rule", + "body": "----------\n", + "description": "Insert horizontal rule" + }, + "Insert link": { + "prefix": "link", + "body": "[${1:text}](${2:link})$0", + "description": "Insert link" + }, + "Insert image": { + "prefix": "image", + "body": "![${1:alt}](${2:link})$0", + "description": "Insert image" + }, + "Insert code chunk": { + "prefix": "code chunk", + "body": [ + "```{${1|language,awk,bash,coffee,gawk,groovy,haskell,lein,mysql,node,octave,perl,psql,Rscript,ruby,sas,scala,sed,sh,stata,zsh,asis,asy,block,block2,bslib,c,cat,cc,comment,css,dot,embed,fortran,fortran95,go,highlight,js,julia,python,R,Rcpp,sass,scss,sql\\, connection=db,stan\\, output.var=\"stanmodel\",targets,tikz,verbatim,exec\\, command=\"\"|}}", + "${TM_SELECTED_TEXT}$0", + "```" + ], + "description": "Insert code chunk" + }, + "Insert R code chunk": { + "prefix": "r code chunk", + "body": [ + "```{r}", + "${TM_SELECTED_TEXT}$0", + "```" + ], + "description": "Insert R code chunk" + }, + "Insert inline R code": { + "prefix": "inline r code", + "body": "`r ${1:${TM_SELECTED_TEXT}}`$0", + "description": "Insert inline R code" + }, + "Insert inline footnotes": { + "prefix": "inline footnotes", + "body": "^[${1:Inline footnotes text.}]$0", + "description": "Insert inline footnotes" + }, + "Insert speaker notes": { + "prefix": "speaker notes", + "body": [ + "::: notes", + "", + "${TM_SELECTED_TEXT}$0", + "", + ":::" + ], + "description": "Insert speaker notes" + }, + "Insert two columns": { + "prefix": "two columns", + "body": [ + ":::::: {.columns}", + "::: {.column}", + "", + "${1:Content of the left column.}", + "", + ":::", + "::: {.column}", + "", + "${2:Content of the right column.}", + "", + ":::", + "::::::$0" + ], + "description": "Insert (side-by-side) two columns" + }, + "Insert incremental list": { + "prefix": "incremental list", + "body": [ + "::: incremental", + "", + "- ${1:first}", + "- ${2:second}", + "- ${3:third}", + "$0", + "", + ":::" + ], + "description": "Insert incremental list" + }, + "Insert nonincremental list": { + "prefix": "nonincremental list", + "body": [ + "::: nonincremental", + "", + "- ${1:first}", + "- ${2:second}", + "- ${3:third}", + "$0", + "", + ":::" + ], + "description": "Insert nonincremental list" + }, + "Insert incremental fenced block": { + "prefix": "incremental fenced block", + "body": [ + "::: incremental", + "", + "${1:${TM_SELECTED_TEXT}}$0", + "", + ":::" + ], + "description": "Insert incremental fenced block" + }, + "Insert nonincremental fenced block": { + "prefix": "nonincremental fenced block", + "body": [ + "::: nonincremental", + "", + "${1:${TM_SELECTED_TEXT}}$0", + "", + ":::" + ], + "description": "Insert nonincremental fenced block" + }, + "Insert reference list": { + "prefix": "reference list", + "body": [ + "::: {#refs}", + ":::$0" + ], + "description": "Insert reference list" + }, + "Insert bookdown theorem": { + "prefix": "theorem", + "body": [ + "::: {.theorem #${1:label} name=\"${2:theorem name}\"}", + "${TM_SELECTED_TEXT}$0", + ":::" + ], + "description": "Insert bookdown theorem" + }, + "Cross-reference bookdown theorem": { + "prefix": "cross-reference theorem", + "body": [ + "Theorem \\@ref(thm:${1:label})$0" + ], + "description": "Cross-reference bookdown theorem" + }, + "Insert bookdown lemma": { + "prefix": "lemma", + "body": [ + "::: {.lemma #${1:label} name=\"${2:lemma name}\"}", + "${TM_SELECTED_TEXT}$0", + ":::" + ], + "description": "Insert bookdown lemma" + }, + "Cross-reference bookdown lemma": { + "prefix": "cross-reference lemma", + "body": [ + "Lemma \\@ref(lem:${1:label})$0" + ], + "description": "Cross-reference bookdown lemma" + }, + "Insert bookdown corollary": { + "prefix": "corollary", + "body": [ + "::: {.corollary #${1:label} name=\"${2:corollary name}\"}", + "${TM_SELECTED_TEXT}$0", + ":::" + ], + "description": "Insert bookdown corollary" + }, + "Cross-reference bookdown corollary": { + "prefix": "cross-reference corollary", + "body": [ + "Corollary \\@ref(cor:${1:label})$0" + ], + "description": "Cross-reference bookdown corollary" + }, + "Insert bookdown proposition": { + "prefix": "proposition", + "body": [ + "::: {.proposition #${1:label} name=\"${2:proposition name}\"}", + "${TM_SELECTED_TEXT}$0", + ":::" + ], + "description": "Insert bookdown proposition" + }, + "Cross-reference bookdown proposition": { + "prefix": "cross-reference proposition", + "body": [ + "Proposition \\@ref(prp:${1:label})$0" + ], + "description": "Cross-reference bookdown proposition" + }, + "Insert bookdown conjecture": { + "prefix": "conjecture", + "body": [ + "::: {.conjecture #${1:label} name=\"${2:conjecture name}\"}", + "${TM_SELECTED_TEXT}$0", + ":::" + ], + "description": "Insert bookdown conjecture" + }, + "Cross-reference bookdown conjecture": { + "prefix": "cross-reference conjecture", + "body": [ + "Conjecture \\@ref(cnj:${1:label})$0" + ], + "description": "Cross-reference bookdown conjecture" + }, + "Insert bookdown definition": { + "prefix": "definition", + "body": [ + "::: {.definition #${1:label} name=\"${2:definition name}\"}", + "${TM_SELECTED_TEXT}$0", + ":::" + ], + "description": "Insert bookdown definition" + }, + "Cross-reference bookdown definition": { + "prefix": "cross-reference definition", + "body": [ + "Definition \\@ref(def:${1:label})$0" + ], + "description": "Cross-reference bookdown definition" + }, + "Insert bookdown example": { + "prefix": "example", + "body": [ + "::: {.example #${1:label} name=\"${2:example name}\"}", + "${TM_SELECTED_TEXT}$0", + ":::" + ], + "description": "Insert bookdown example" + }, + "Cross-reference bookdown example": { + "prefix": "cross-reference example", + "body": [ + "Example \\@ref(exm:${1:label})$0" + ], + "description": "Cross-reference bookdown example" + }, + "Insert bookdown exercise": { + "prefix": "exercise", + "body": [ + "::: {.exercise #${1:label} name=\"${2:exercise name}\"}", + "${TM_SELECTED_TEXT}$0", + ":::" + ], + "description": "Insert bookdown exercise" + }, + "Cross-reference bookdown exercise": { + "prefix": "cross-reference exercise", + "body": [ + "Exercise \\@ref(exr:${1:label})$0" + ], + "description": "Cross-reference bookdown exercise" + }, + "Insert bookdown hypothesis": { + "prefix": "hypothesis", + "body": [ + "::: {.hypothesis #${1:label} name=\"${2:hypothesis name}\"}", + "${TM_SELECTED_TEXT}$0", + ":::" + ], + "description": "Insert bookdown hypothesis" + }, + "Cross-reference bookdown hypothesis": { + "prefix": "cross-reference hypothesis", + "body": [ + "Hypothesis \\@ref(hyp:${1:label})$0" + ], + "description": "Cross-reference bookdown hypothesis" + }, + "Insert bookdown equation": { + "prefix": "equation", + "body": [ + "\\begin{equation}", + "${TM_SELECTED_TEXT}$0", + "(\\#eq:${1:label})", + "\\end{equation}" + ], + "description": "Insert bookdown equation" + }, + "Cross-reference bookdown equation": { + "prefix": "cross-reference equation", + "body": [ + "Equation \\@ref(eq:${1:label})$0" + ], + "description": "Cross-reference bookdown equation" + }, + "Cross-reference bookdown table": { + "prefix": "cross-reference table", + "body": [ + "Table \\@ref(tab:${1:label})$0" + ], + "description": "Cross-reference bookdown table" + }, + "Insert figure chunk": { + "prefix": "figure chunk", + "body": [ + "```{r ${1:label}, fig.cap = \"${2:Figure caption.}\", fig.alt = \"${3:Alt text.}\"}", + "${TM_SELECTED_TEXT}$0", + "```" + ], + "description": "Insert figure chunk" + }, + "Insert external figure chunk": { + "prefix": "external figure chunk", + "body": [ + "```{r ${1:label}, fig.cap = \"${2:Figure caption.}\", fig.alt = \"${3:Alt text.}\"}", + "knitr::include_graphics(\"${4:path/to/file.png}\")", + "```" + ], + "description": "Insert external figure chunk" + }, + "Cross-reference bookdown figure": { + "prefix": "cross-reference figure", + "body": [ + "Figure \\@ref(fig:${1:label})$0" + ], + "description": "Cross-reference bookdown figure" + }, + "Insert bookdown section label": { + "prefix": "section label", + "body": [ + "${TM_SELECTED_TEXT} {#${1:label}}$0" + ], + "description": "Insert bookdown section label" + }, + "Cross-reference bookdown section": { + "prefix": "cross-reference section", + "body": [ + "Section \\@ref(${1:label})$0" + ], + "description": "Cross-reference bookdown section" + }, + "Insert bookdown part": { + "prefix": "part", + "body": [ + "# (PART) Part ${1:I} {-}$0" + ], + "description": "Insert bookdown part" + }, + "Insert unnumbered bookdown part": { + "prefix": "unnumbered part", + "body": [ + "# (PART\\*) Part ${1:I} {-}$0" + ], + "description": "Insert bookdown unnumbered part" + }, + "Insert bookdown appendix": { + "prefix": "appendix", + "body": [ + "# (APPENDIX) Appendix {-}$0" + ], + "description": "Insert bookdown appendix" + } +} \ No newline at end of file diff --git a/src/api.d.ts b/src/api.d.ts new file mode 100644 index 000000000..b282df1b7 --- /dev/null +++ b/src/api.d.ts @@ -0,0 +1,20 @@ + +// declaration of the api exported by the extension +// implemented in apiImplementation.ts +// used e.g. by vscode-r-debugger to show the help panel from within debug sessions + + +export declare class RExtension { + helpPanel?: HelpPanel; +} + +export type HelpSubMenu = 'doc' | 'pkgList' | 'refresh' | '?' | '??'; + +export interface HelpPanel { + showHelpForPath(requestPath?: string): void; + dispose(): void; + refresh(): void; +} + + + diff --git a/src/apiImplementation.ts b/src/apiImplementation.ts new file mode 100644 index 000000000..d3f71518a --- /dev/null +++ b/src/apiImplementation.ts @@ -0,0 +1,8 @@ +import { RHelp } from './helpViewer'; +import { RExtension } from './api'; +export class RExtensionImplementation implements RExtension { + public helpPanel?: RHelp; +} + + + diff --git a/src/completions.ts b/src/completions.ts new file mode 100644 index 000000000..17546f42e --- /dev/null +++ b/src/completions.ts @@ -0,0 +1,332 @@ +/* eslint-disable @typescript-eslint/no-unsafe-call */ +/* eslint-disable @typescript-eslint/no-unsafe-assignment */ +/* eslint-disable @typescript-eslint/no-unsafe-return */ +/* eslint-disable @typescript-eslint/restrict-template-expressions */ +/* eslint-disable @typescript-eslint/no-unsafe-member-access */ + + +import * as vscode from 'vscode'; + +import * as session from './session'; +import { extendSelection } from './selection'; +import { cleanLine } from './lineCache'; +import { globalRHelp } from './extension'; +import { config } from './util'; +import { getChunks } from './rmarkdown'; +import { CompletionItemKind } from 'vscode-languageclient'; + + +// Get with names(roxygen2:::default_tags()) +const roxygenTagCompletionItems = [ + 'export', 'exportClass', 'exportMethod', 'exportPattern', 'import', 'importClassesFrom', + 'importFrom', 'importMethodsFrom', 'rawNamespace', 'S3method', 'useDynLib', 'aliases', + 'author', 'backref', 'concept', 'describeIn', 'description', 'details', + 'docType', 'encoding', 'evalRd', 'example', 'examples', 'family', + 'field', 'format', 'inherit', 'inheritParams', 'inheritDotParams', 'inheritSection', + 'keywords', 'method', 'name', 'md', 'noMd', 'noRd', + 'note', 'param', 'rdname', 'rawRd', 'references', 'return', + 'section', 'seealso', 'slot', 'source', 'template', 'templateVar', + 'title', 'usage' +].map((x: string) => new vscode.CompletionItem(`${x} `)); + + +export class HoverProvider implements vscode.HoverProvider { + async provideHover(document: vscode.TextDocument, position: vscode.Position): Promise { + if(!session.workspaceData?.globalenv){ + return null; + } + + if (document.languageId === 'rmd') { + const chunks = getChunks(document); + const chunk = chunks.find((chunk) => chunk.language === 'r' && chunk.startLine < position.line && chunk.endLine > position.line); + if (!chunk) { + return null; + } + } + + let hoverRange = document.getWordRangeAtPosition(position); + let hoverText = null; + + if (session.server) { + const exprRegex = /([a-zA-Z0-9._$@ ])+(? { + if(!config().get('helpPanel.enableHoverLinks')){ + return null; + } + + if (document.languageId === 'rmd') { + const chunks = getChunks(document); + const chunk = chunks.find((chunk) => chunk.language === 'r' && chunk.startLine < position.line && chunk.endLine > position.line); + if (!chunk) { + return null; + } + } + + const re = /([a-zA-Z0-9._:])+/; + const wordRange = document.getWordRangeAtPosition(position, re); + const token = document.getText(wordRange); + const aliases = await globalRHelp?.getMatchingAliases(token) || []; + const mds = aliases.map(a => { + const cmdText = `${a.package}::${a.alias}`; + const args = [`/library/${a.package}/html/${a.name}.html`]; + const encodedArgs = encodeURIComponent(JSON.stringify(args)); + const cmd = 'command:r.helpPanel.openForPath'; + const cmdUri = vscode.Uri.parse(`${cmd}?${encodedArgs}`); + return `[\`${cmdText}\`](${cmdUri})`; + }); + const md = new vscode.MarkdownString(mds.join(' \n')); + md.isTrusted = true; + return new vscode.Hover(md, wordRange); + } +} + + +export class StaticCompletionItemProvider implements vscode.CompletionItemProvider { + provideCompletionItems(document: vscode.TextDocument, position: vscode.Position): vscode.CompletionItem[] | undefined { + if (document.languageId === 'rmd') { + const chunks = getChunks(document); + const chunk = chunks.find((chunk) => chunk.language === 'r' && chunk.startLine < position.line && chunk.endLine > position.line); + if (!chunk) { + return undefined; + } + } + + if (document.lineAt(position).text.startsWith('#\'')) { + return roxygenTagCompletionItems; + } + + return undefined; + } +} + + +export class LiveCompletionItemProvider implements vscode.CompletionItemProvider { + async provideCompletionItems( + document: vscode.TextDocument, + position: vscode.Position, + token: vscode.CancellationToken, + completionContext: vscode.CompletionContext + ): Promise { + const items: vscode.CompletionItem[] = []; + if (token.isCancellationRequested || !session.workspaceData?.globalenv) { + return items; + } + + if (document.languageId === 'rmd') { + const chunks = getChunks(document); + const chunk = chunks.find((chunk) => chunk.language === 'r' && chunk.startLine < position.line && chunk.endLine > position.line); + if (!chunk) { + return items; + } + } + + const trigger = completionContext.triggerCharacter; + + if (trigger === undefined) { + Object.keys(session.workspaceData.globalenv).forEach((key) => { + const obj = session.workspaceData.globalenv[key]; + const item = new vscode.CompletionItem( + key, + obj.type === 'closure' || obj.type === 'builtin' + ? vscode.CompletionItemKind.Function + : vscode.CompletionItemKind.Field + ); + item.detail = '[session]'; + item.documentation = new vscode.MarkdownString(`\`\`\`r\n${obj.str}\n\`\`\``); + items.push(item); + }); + } else if(trigger === '$' || trigger === '@') { + const symbolPosition = new vscode.Position(position.line, position.character - 1); + if (session.server) { + const re = /([a-zA-Z0-9._$@ ])+(? { + const item = new vscode.CompletionItem(e.name, (e.type === 'closure' || e.type === 'builtin') ? CompletionItemKind.Function : vscode.CompletionItemKind.Variable); + item.detail = detail; + item.documentation = new vscode.MarkdownString(`\`\`\`r\n${e.str}\n\`\`\``); + item.sortText = `0-${index.toString().padStart(len, '0')}`; + index++; + return item; + }); +} + +function getCompletionItems(names: string[], kind: vscode.CompletionItemKind, detail: string, documentation: vscode.MarkdownString): vscode.CompletionItem[] { + const len = names.length.toString().length; + let index = 0; + return names.map((name) => { + const item = new vscode.CompletionItem(name, kind); + item.detail = detail; + item.documentation = documentation; + item.sortText = `0-${index.toString().padStart(len, '0')}`; + index++; + return item; + }); +} + +function getBracketCompletionItems(document: vscode.TextDocument, position: vscode.Position, token: vscode.CancellationToken): vscode.CompletionItem[] { + const items: vscode.CompletionItem[] = []; + let range: vscode.Range | undefined = new vscode.Range(new vscode.Position(position.line, 0), position); + let expectOpenBrackets = 0; + let symbol: string | undefined = undefined; + + while (range) { + if (token.isCancellationRequested) { return []; } + const text = document.getText(range); + for (let i = text.length - 1; i >= 0; i -= 1) { + const chr = text.charAt(i); + if (chr === ']') { + expectOpenBrackets += 1; + } else if (chr === '[') { + if (expectOpenBrackets === 0) { + const symbolPosition = new vscode.Position(range.start.line, i - 1); + const symbolRange = document.getWordRangeAtPosition(symbolPosition); + symbol = document.getText(symbolRange); + range = undefined; + break; + } else { + expectOpenBrackets -= 1; + } + } + } + if (range?.start?.line !== undefined && range.start.line > 0) { + range = document.lineAt(range.start.line - 1).range; // check previous line + } else { + range = undefined; + } + } + + if (!token.isCancellationRequested && symbol !== undefined) { + const obj = session.workspaceData.globalenv[symbol]; + if (obj !== undefined && obj.names !== undefined) { + const doc = new vscode.MarkdownString('Element of `' + symbol + '`'); + items.push(...getCompletionItems(obj.names, vscode.CompletionItemKind.Variable, '[session]', doc)); + } + } + return items; +} + +function getPipelineCompletionItems(document: vscode.TextDocument, position: vscode.Position, token: vscode.CancellationToken): vscode.CompletionItem[] { + const items: vscode.CompletionItem[] = []; + const range = extendSelection(position.line, (x) => document.lineAt(x).text, document.lineCount); + let symbol: string | undefined = undefined; + + for (let i = range.startLine; i <= range.endLine; i++) { + if (token.isCancellationRequested) { + break; + } + + const line = document.lineAt(i); + if (line.isEmptyOrWhitespace) { + continue; + } + + const cleanedLine = cleanLine(line.text); + if (cleanedLine.length === 0) { + continue; + } + + const pipeSymbolIndex = line.text.search(/([\w_.]+)\s*(%.+%|\|>)/); + if (pipeSymbolIndex < 0) { + break; + } + + const symbolPosition = new vscode.Position(i, pipeSymbolIndex); + const symbolRange = document.getWordRangeAtPosition(symbolPosition); + + if (symbolRange !== undefined) { + symbol = document.getText(symbolRange); + } + + break; + } + + if (!token.isCancellationRequested && symbol !== undefined) { + const obj = session.workspaceData.globalenv[symbol]; + if (obj !== undefined && obj.names !== undefined) { + const doc = new vscode.MarkdownString('Element of `' + symbol + '`'); + items.push(...getCompletionItems(obj.names, vscode.CompletionItemKind.Variable, '[session]', doc)); + } + } + return items; +} diff --git a/src/cppProperties.ts b/src/cppProperties.ts new file mode 100644 index 000000000..ef477ef1b --- /dev/null +++ b/src/cppProperties.ts @@ -0,0 +1,209 @@ +'use strict'; + +import * as fs from 'fs'; +import * as path from 'path'; +import { window } from 'vscode'; +import { getRpath, getCurrentWorkspaceFolder, executeRCommand, createTempDir } from './util'; +import { execSync } from 'child_process'; +import { extensionContext } from './extension'; + +export async function generateCppProperties(): Promise { + const currentWorkspaceFolder = getCurrentWorkspaceFolder()?.uri.fsPath; + if (currentWorkspaceFolder === undefined) { + void window.showWarningMessage('Please open a workspace folder to create c_cpp_properties.json'); + return; + } + const outFilePath = path.join(currentWorkspaceFolder, '.vscode', 'c_cpp_properties.json'); + if (fs.existsSync(outFilePath)) { + const overwrite = await window.showWarningMessage( + '"c_cpp_properties.json" file already exists. Do you want to overwrite?', + 'Yes', 'No' + ); + if (overwrite === 'No') { + return; + } + void fs.unlinkSync(outFilePath); + } + return generateCppPropertiesProc(currentWorkspaceFolder); +} + +/** Helper: Return object depending on current process platform */ +function platformChoose(win32: A, darwin: B, other: C): A | B | C { + return process.platform === 'win32' ? win32 : + process.platform === 'darwin' ? darwin : + other; +} + +// See: https://code.visualstudio.com/docs/cpp/c-cpp-properties-schema-reference +async function generateCppPropertiesProc(workspaceFolder: string) { + const rPath = await getRpath(); + if (!rPath) { + return; + } + + // Collect information from running the compiler + const configureFile = platformChoose('configure.win', 'configure', 'configure'); + const cleanupFile = platformChoose('cleanup.win', 'cleanup', 'cleanup'); + + if (fs.existsSync(path.join(workspaceFolder, configureFile))) { + await executeRCommand(`system("sh ./${configureFile}")`, workspaceFolder, (e: Error) => { + void window.showErrorMessage(e.message); + return ''; + }); + } + + const compileOutputCpp = collectCompilerOutput(rPath, workspaceFolder, 'cpp'); + const compileOutputC = collectCompilerOutput(rPath, workspaceFolder, 'c'); + + if (fs.existsSync(path.join(workspaceFolder, cleanupFile))) { + await executeRCommand(`system("sh ./${cleanupFile}")`, workspaceFolder, (e: Error) => { + void window.showErrorMessage(e.message); + return ''; + }); + } + + const compileInfo = extractCompilerInfo(compileOutputCpp); + const compileStdCpp = extractCompilerStd(compileOutputCpp); + const compileStdC = extractCompilerStd(compileOutputC); + const compileCall = extractCompilerCall(compileOutputCpp); + const compilerPath = compileCall ? await executeRCommand(`cat(Sys.which("${compileCall}"))`, workspaceFolder, (e: Error) => { + void window.showErrorMessage(e.message); + return ''; + }) : ''; + + const intelliSensePlatform = platformChoose('windows', 'macos', 'linux'); + const intelliSenseComp = compileCall ? (compileCall.includes('clang') ? 'clang' : 'gcc') : 'gcc'; + const intelliSense = `${intelliSensePlatform}-${intelliSenseComp}-${process.arch}`; + + // Collect information from 'DESCRIPTION' + const linkingToIncludes = await collectRLinkingTo(workspaceFolder); + + // Combine information + const envIncludes: string[] = ['${workspaceFolder}/src']; + envIncludes.push(...compileInfo.compIncludes.map((v) => path.isAbsolute(v) ? v : `\${workspaceFolder}/${path.join('src', v)}`)); + envIncludes.push(...linkingToIncludes); + + const envDefines = compileInfo.compDefines; + + // If no standard is set on linux, the C standard seems to default to the c++ one. + const envCStd = (!compileStdC || compileStdC.includes('++')) ? '${default}' : compileStdC; + + const platformName = platformChoose('Win32', 'Mac', 'Linux'); + + // Build json + const re = { + 'configurations': [{ + 'name': platformName, + 'defines': envDefines, + 'includePath': envIncludes, + 'compilerPath': compilerPath, + 'cStandard': envCStd, + 'cppStandard': compileStdCpp, + 'intelliSenseMode': intelliSense + }], + 'version': 4 + }; + const ser = JSON.stringify(re, null, 2); + + // Write file + const vscodeDir = path.join(workspaceFolder, '.vscode'); + if (!fs.existsSync(vscodeDir)) { + fs.mkdirSync(vscodeDir); + } + fs.writeFileSync(path.join(vscodeDir, 'c_cpp_properties.json'), ser); +} + +async function collectRLinkingTo(workspaceFolder: string): Promise { + if (!fs.existsSync(path.join(workspaceFolder, 'DESCRIPTION'))) { + return []; + } + + const rScript = extensionContext.asAbsolutePath('R/cppProperties/extractLinkingTo.R').replace(/\\/g, '/'); + const linkingToIncludesStr = (await executeRCommand(`source('${rScript}')`, workspaceFolder, (e: Error) => { + void window.showErrorMessage(e.message); + return ''; + }))?.trim(); + if (!linkingToIncludesStr || linkingToIncludesStr === '') { + return []; + } + return linkingToIncludesStr.split(/\r?\n/g).map(decodeURI); +} + +function ensureUnquoted(str: string): string { + if (/(^".*"$)|(^'.*'$)/.test(str)) { + return str.substring(1, str.length - 1); + } + return str; +} + +function extractCompilerInfo(compileOutput: string) { + const rxCompArg = /-(I|D)("[^"]+"|[\S]+)/gm; + + const compDefines: string[] = []; + const compIncludes: string[] = []; + const compLookup = { 'D': compDefines, 'I': compIncludes }; + + let m: RegExpExecArray | null; + while ((m = rxCompArg.exec(compileOutput)) !== null) { + if (m.index === rxCompArg.lastIndex) { + rxCompArg.lastIndex++; + } + + // The regex guarantees that the first group is 'I' or 'D' + compLookup[(m[1] as 'D' | 'I')].push(ensureUnquoted(m[2])); + } + + return { + compDefines: compDefines, + compIncludes: compIncludes + }; +} + +function extractCompilerStd(compileOutput: string): string | undefined { + const rxStd = /-std=(\S+)/; + + const stdMatch = compileOutput.match(rxStd); + return stdMatch?.[1]; +} + +function extractCompilerCall(compileOutput: string): string | undefined { + const rxComp = /("[^"]+"|[\S]+)/; + const ccalls = compileOutput.split('\n'); + if (ccalls.length < 2) { + return undefined; + } + + const m = ccalls[1].match(rxComp); + return m?.[1]; +} + +function collectCompilerOutput(rPath: string, workspaceFolder: string, testExtension: 'cpp' | 'c') { + + const makevarsFiles = ['Makevars', 'Makevars.win', 'Makevars.ucrt']; + + const srcFolder = path.join(workspaceFolder, 'src'); + const tempFolder = createTempDir(workspaceFolder); + + // Copy makevars + if (fs.existsSync(srcFolder)) { + const projectMakevarsFiles = fs.readdirSync(srcFolder).filter(fn => makevarsFiles.includes(fn)); + for (const f of projectMakevarsFiles) { + fs.copyFileSync(path.join(srcFolder, f), path.join(tempFolder, f)); + } + } + + // Create dummy source file + const testFile = `comp_test.${testExtension}`; + fs.writeFileSync(path.join(tempFolder, testFile), ''); + + // Compile dummy + const command = `"${rPath}" CMD SHLIB ${testFile}`; + const compileOutput = execSync(command, { + cwd: tempFolder + }).toString(); + + // Cleanup + fs.rmSync(tempFolder, { recursive: true, force: true }); + + return compileOutput; +} diff --git a/src/extension.ts b/src/extension.ts index c27d1f9eb..67a07ea07 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -1,135 +1,259 @@ -"use strict"; -// The module 'vscode' contains the VS Code extensibility API -// Import the module and reference it with the alias vscode in your code below -import { isNull } from "util"; -import { commands, CompletionItem, ExtensionContext, IndentAction, - languages, Position, TextDocument, window } from "vscode"; -import { previewDataframe, previewEnvironment } from "./preview"; -import { createGitignore } from "./rGitignore"; -import { chooseTerminal, chooseTerminalAndSendText, createRTerm, deleteTerminal, - runSelectionInTerm } from "./rTerminal"; -import { config, ToRStringLiteral } from "./util"; - -const wordPattern = /(-?\d*\.\d\w*)|([^\`\~\!\@\$\^\&\*\(\)\=\+\[\{\]\}\\\|\;\:\'\"\,\<\>\/\s]+)/g; - -// Get with names(roxygen2:::default_tags()) -const roxygenTagCompletionItems = [ - "export", "exportClass", "exportMethod", "exportPattern", "import", "importClassesFrom", - "importFrom", "importMethodsFrom", "rawNamespace", "S3method", "useDynLib", "aliases", - "author", "backref", "concept", "describeIn", "description", "details", - "docType", "encoding", "evalRd", "example", "examples", "family", - "field", "format", "inherit", "inheritParams", "inheritDotParams", "inheritSection", - "keywords", "method", "name", "md", "noMd", "noRd", - "note", "param", "rdname", "rawRd", "references", "return", - "section", "seealso", "slot", "source", "template", "templateVar", - "title", "usage"].map((x) => new CompletionItem(x + " ")); - -// This method is called when your extension is activated -// Your extension is activated the very first time the command is executed -export function activate(context: ExtensionContext) { - // Use the console to output diagnostic information (console.log) and errors (console.error) - // This line of code will only be executed once when your extension is activated - - // The command has been defined in the package.json file - // Now provide the implementation of the command with registerCommand - // The commandId parameter must match the command field in package.json - - function runSource(echo: boolean) { - const wad = window.activeTextEditor.document; - wad.save(); - let rPath = ToRStringLiteral(wad.fileName, '"'); - let encodingParam = config.get("source.encoding") as string; - if (encodingParam) { - encodingParam = `encoding = "${encodingParam}"`; - rPath = [rPath, encodingParam].join(", "); - } - if (echo) { - rPath = [rPath, "echo = TRUE"].join(", "); - } - chooseTerminalAndSendText(`source(${rPath})`); - } - function knitRmd(echo: boolean, outputFormat: string) { - const wad = window.activeTextEditor.document; - wad.save(); - let rPath = ToRStringLiteral(wad.fileName, '"'); - let encodingParam = config.get("source.encoding") as string; - if (encodingParam) { - encodingParam = `encoding = "${encodingParam}"`; - rPath = [rPath, encodingParam].join(", "); - } - if (echo) { - rPath = [rPath, "echo = TRUE"].join(", "); - } - if (isNull(outputFormat)) { - chooseTerminalAndSendText(`rmarkdown::render(${rPath})`); - } else { - chooseTerminalAndSendText(`rmarkdown::render(${rPath}, "${outputFormat}")`); - } +'use strict'; + +// interfaces, functions, etc. provided by vscode +import * as vscode from 'vscode'; +import * as os from 'os'; +import path = require('path'); + +// functions etc. implemented in this extension +import * as preview from './preview'; +import * as rGitignore from './rGitignore'; +import * as lintrConfig from './lintrConfig'; +import * as cppProperties from './cppProperties'; +import * as rTerminal from './rTerminal'; +import * as session from './session'; +import * as util from './util'; +import * as rstudioapi from './rstudioapi'; +import * as rmarkdown from './rmarkdown'; +import * as workspaceViewer from './workspaceViewer'; +import * as apiImplementation from './apiImplementation'; +import * as rHelp from './helpViewer'; +import * as completions from './completions'; +import * as rShare from './liveShare'; +import * as httpgdViewer from './plotViewer'; +import * as languageService from './languageService'; +import { RTaskProvider } from './tasks'; + + +// global objects used in other files +export const homeExtDir = (): string => util.getDir(path.join(os.homedir(), '.vscode-R')); +export const tmpDir = (): string => util.getDir(path.join(homeExtDir(), 'tmp')); +export let rWorkspace: workspaceViewer.WorkspaceDataProvider | undefined = undefined; +export let globalRHelp: rHelp.RHelp | undefined = undefined; +export let extensionContext: vscode.ExtensionContext; +export let enableSessionWatcher: boolean | undefined = undefined; +export let globalHttpgdManager: httpgdViewer.HttpgdManager | undefined = undefined; +export let rmdPreviewManager: rmarkdown.RMarkdownPreviewManager | undefined = undefined; +export let rmdKnitManager: rmarkdown.RMarkdownKnitManager | undefined = undefined; +export let sessionStatusBarItem: vscode.StatusBarItem | undefined = undefined; + +// Called (once) when the extension is activated +export async function activate(context: vscode.ExtensionContext): Promise { + if (vscode.extensions.getExtension('mikhail-arkhipov.r')) { + void vscode.window.showInformationMessage('The R Tools (Mikhail-Arkhipov.r) extension is enabled and will have conflicts with vscode-R. To use vscode-R, please disable or uninstall the extension.'); + void vscode.commands.executeCommand('workbench.extensions.search', '@installed R Tools'); } - async function runSelection(rFunctionName: string[]) { - const callableTerminal = await chooseTerminal(); - if (isNull(callableTerminal)) { - return; - } - runSelectionInTerm(callableTerminal, rFunctionName); + // create a new instance of RExtensionImplementation + // is used to export an interface to the help panel + // this export is used e.g. by vscode-r-debugger to show the help panel from within debug sessions + const rExtension = new apiImplementation.RExtensionImplementation(); + + // assign extension context to global variable + extensionContext = context; + + // assign session watcher setting to global variable + enableSessionWatcher = util.config().get('sessionWatcher') ?? false; + rmdPreviewManager = new rmarkdown.RMarkdownPreviewManager(); + rmdKnitManager = new rmarkdown.RMarkdownKnitManager(); + + + // register commands specified in package.json + const commands = { + // create R terminal + 'r.createRTerm': rTerminal.createRTerm, + + // run code from editor in terminal + 'r.nrow': () => rTerminal.runSelectionOrWord(['nrow']), + 'r.length': () => rTerminal.runSelectionOrWord(['length']), + 'r.head': () => rTerminal.runSelectionOrWord(['head']), + 'r.thead': () => rTerminal.runSelectionOrWord(['t', 'head']), + 'r.names': () => rTerminal.runSelectionOrWord(['names']), + 'r.view': () => rTerminal.runSelectionOrWord(['View']), + 'r.runSource': () => { void rTerminal.runSource(false); }, + 'r.runSelection': (code?: string) => { code ? void rTerminal.runTextInTerm(code) : void rTerminal.runSelection(); }, + 'r.runFromLineToEnd': rTerminal.runFromLineToEnd, + 'r.runFromBeginningToLine': rTerminal.runFromBeginningToLine, + 'r.runSelectionRetainCursor': rTerminal.runSelectionRetainCursor, + 'r.runCommandWithSelectionOrWord': rTerminal.runCommandWithSelectionOrWord, + 'r.runCommandWithEditorPath': rTerminal.runCommandWithEditorPath, + 'r.runCommand': rTerminal.runCommand, + 'r.runSourcewithEcho': () => { void rTerminal.runSource(true); }, + + // chunk related + 'r.selectCurrentChunk': rmarkdown.selectCurrentChunk, + 'r.runCurrentChunk': rmarkdown.runCurrentChunk, + 'r.runCurrentChunkAndMove': rmarkdown.runCurrentChunkAndMove, + 'r.runPreviousChunk': rmarkdown.runPreviousChunk, + 'r.runNextChunk': rmarkdown.runNextChunk, + 'r.runAboveChunks': rmarkdown.runAboveChunks, + 'r.runCurrentAndBelowChunks': rmarkdown.runCurrentAndBelowChunks, + 'r.runBelowChunks': rmarkdown.runBelowChunks, + 'r.runAllChunks': rmarkdown.runAllChunks, + 'r.goToPreviousChunk': rmarkdown.goToPreviousChunk, + 'r.goToNextChunk': rmarkdown.goToNextChunk, + 'r.runChunks': rTerminal.runChunksInTerm, + + // rmd related + 'r.knitRmd': () => { void rmdKnitManager?.knitRmd(false, undefined); }, + 'r.knitRmdToPdf': () => { void rmdKnitManager?.knitRmd(false, 'pdf_document'); }, + 'r.knitRmdToHtml': () => { void rmdKnitManager?.knitRmd(false, 'html_document'); }, + 'r.knitRmdToAll': () => { void rmdKnitManager?.knitRmd(false, 'all'); }, + + 'r.rmarkdown.newDraft': () => rmarkdown.newDraft(), + 'r.rmarkdown.setKnitDirectory': () => rmdKnitManager?.setKnitDir(), + 'r.rmarkdown.showPreviewToSide': () => rmdPreviewManager?.previewRmd(vscode.ViewColumn.Beside), + 'r.rmarkdown.showPreview': (uri: vscode.Uri) => rmdPreviewManager?.previewRmd(vscode.ViewColumn.Active, uri), + 'r.rmarkdown.preview.refresh': () => rmdPreviewManager?.updatePreview(), + 'r.rmarkdown.preview.openExternal': () => void rmdPreviewManager?.openExternalBrowser(), + 'r.rmarkdown.preview.showSource': () => rmdPreviewManager?.showSource(), + 'r.rmarkdown.preview.toggleStyle': () => rmdPreviewManager?.toggleTheme(), + 'r.rmarkdown.preview.enableAutoRefresh': () => rmdPreviewManager?.enableAutoRefresh(), + 'r.rmarkdown.preview.disableAutoRefresh': () => rmdPreviewManager?.disableAutoRefresh(), + + // file creation (under file submenu) + 'r.rmarkdown.newFileDraft': () => rmarkdown.newDraft(), + 'r.newFileDocument': () => vscode.workspace.openTextDocument({language: 'r'}).then((v) => vscode.window.showTextDocument(v)), + + // editor independent commands + 'r.createGitignore': rGitignore.createGitignore, + 'r.createLintrConfig': lintrConfig.createLintrConfig, + 'r.generateCCppProperties': cppProperties.generateCppProperties, + 'r.loadAll': () => rTerminal.runTextInTerm('devtools::load_all()'), + + // environment independent commands. this is a workaround for using the Tasks API: https://github.com/microsoft/vscode/issues/40758 + 'r.build': () => vscode.commands.executeCommand('workbench.action.tasks.runTask', 'R: Build'), + 'r.buildBinary': () => vscode.commands.executeCommand('workbench.action.tasks.runTask', 'R: Build Binary'), + 'r.check': () => vscode.commands.executeCommand('workbench.action.tasks.runTask', 'R: Check'), + 'r.document': () => vscode.commands.executeCommand('workbench.action.tasks.runTask', 'R: Document'), + 'r.install': () => vscode.commands.executeCommand('workbench.action.tasks.runTask', 'R: Install'), + 'r.test': () => vscode.commands.executeCommand('workbench.action.tasks.runTask', 'R: Test'), + + // interaction with R sessions + 'r.previewDataframe': preview.previewDataframe, + 'r.previewEnvironment': preview.previewEnvironment, + 'r.attachActive': session.attachActive, + 'r.launchAddinPicker': rstudioapi.launchAddinPicker, + + // workspace viewer + 'r.workspaceViewer.refreshEntry': () => rWorkspace?.refresh(), + 'r.workspaceViewer.view': (node: workspaceViewer.GlobalEnvItem) => node?.label && workspaceViewer.viewItem(node.label), + 'r.workspaceViewer.remove': (node: workspaceViewer.GlobalEnvItem) => node?.label && workspaceViewer.removeItem(node.label), + 'r.workspaceViewer.clear': workspaceViewer.clearWorkspace, + 'r.workspaceViewer.load': workspaceViewer.loadWorkspace, + 'r.workspaceViewer.save': workspaceViewer.saveWorkspace, + + // browser controls + 'r.browser.refresh': session.refreshBrowser, + 'r.browser.openExternal': session.openExternalBrowser, + + // (help related commands are registered in rHelp.initializeHelp) + }; + for (const [key, value] of Object.entries(commands)) { + context.subscriptions.push(vscode.commands.registerCommand(key, value)); } - async function runSelectionInActiveTerm(rFunctionName: string[]) { - const callableTerminal = await chooseTerminal(true); - if (isNull(callableTerminal)) { - return; + + // keep track of terminals + context.subscriptions.push(vscode.window.onDidCloseTerminal(rTerminal.deleteTerminal)); + + // start language service + if (util.config().get('lsp.enabled')) { + const lsp = vscode.extensions.getExtension('reditorsupport.r-lsp'); + if (lsp) { + void vscode.window.showInformationMessage('The R language server extension has been integrated into vscode-R. You need to disable or uninstall REditorSupport.r-lsp and reload window to use the new version.'); + void vscode.commands.executeCommand('workbench.extensions.search', '@installed r-lsp'); + } else { + context.subscriptions.push(new languageService.LanguageService()); } - runSelectionInTerm(callableTerminal, rFunctionName); } - languages.registerCompletionItemProvider("r", { - provideCompletionItems(document: TextDocument, position: Position) { - if (document.lineAt(position).text.substr(0, 2) === "#'") { - return roxygenTagCompletionItems; - } else { - return undefined; - } - }, - }, "@"); // Trigger on '@' - - languages.setLanguageConfiguration("r", { - onEnterRules: [{ // Automatically continue roxygen comments: #' - action: { indentAction: IndentAction.None, appendText: "#' " }, - beforeText: /^#'.*/, - }], + // register on-enter rule for roxygen comments + const wordPattern = /(-?\d*\.\d\w*)|([^`~!@$^&*()=+[{\]}\\|;:'",<>/\s]+)/g; + vscode.languages.setLanguageConfiguration('r', { + onEnterRules: [ + { + // Automatically continue roxygen comments: #' + action: { indentAction: vscode.IndentAction.None, appendText: '#\' ' }, + beforeText: /^\s*#'\s*[^\s]/, // matches a non-empty roxygen line + }, + { + // Automatically continue roxygen comments: #' + action: { indentAction: vscode.IndentAction.None, appendText: '#\' ' }, + beforeText: /^\s*#'/, // matches any roxygen comment line, even an empty one + previousLineText: /^\s*([^#\s].*|#[^'\s].*|#'\s*[^\s].*|)$/, // matches everything but an empty roxygen line + }, + ], wordPattern, }); - context.subscriptions.push( - commands.registerCommand("r.nrow", () => runSelection(["nrow"])), - commands.registerCommand("r.length", () => runSelection(["length"])), - commands.registerCommand("r.head", () => runSelection(["head"])), - commands.registerCommand("r.thead", () => runSelection(["t", "head"])), - commands.registerCommand("r.names", () => runSelection(["names"])), - commands.registerCommand("r.runSource", () => runSource(false)), - commands.registerCommand("r.knitRmd", () => knitRmd(false, null)), - commands.registerCommand("r.knitRmdToPdf", () => knitRmd(false, "pdf_document")), - commands.registerCommand("r.knitRmdToHtml", () => knitRmd(false, "html_document")), - commands.registerCommand("r.knitRmdToAll", () => knitRmd(false, "all")), - commands.registerCommand("r.createRTerm", createRTerm), - commands.registerCommand("r.runSourcewithEcho", () => runSource(true)), - commands.registerCommand("r.runSelection", () => runSelection([])), - commands.registerCommand("r.runSelectionInActiveTerm", () => runSelectionInActiveTerm([])), - commands.registerCommand("r.createGitignore", createGitignore), - commands.registerCommand("r.previewDataframe", previewDataframe), - commands.registerCommand("r.previewEnvironment", previewEnvironment), - commands.registerCommand("r.loadAll", () => chooseTerminalAndSendText("devtools::load_all()")), - commands.registerCommand("r.test", () => chooseTerminalAndSendText("devtools::test()")), - commands.registerCommand("r.install", () => chooseTerminalAndSendText("devtools::install()")), - commands.registerCommand("r.build", () => chooseTerminalAndSendText("devtools::build()")), - commands.registerCommand("r.document", () => chooseTerminalAndSendText("devtools::document()")), - window.onDidCloseTerminal(deleteTerminal), - ); -} + // register terminal-provider + context.subscriptions.push(vscode.window.registerTerminalProfileProvider('r.terminal-profile', + { + async provideTerminalProfile() { + return { + options: await rTerminal.makeTerminalOptions() + }; + } + } + )); + + // initialize httpgd viewer + globalHttpgdManager = httpgdViewer.initializeHttpgd(); + + // initialize the package/help related functions + globalRHelp = await rHelp.initializeHelp(context, rExtension); -// This method is called when your extension is deactivated -// export function deactivate() { + // register codelens and completion providers for r markdown and r files + vscode.languages.registerCodeLensProvider(['r', 'rmd'], new rmarkdown.RMarkdownCodeLensProvider()); + vscode.languages.registerCompletionItemProvider('rmd', new rmarkdown.RMarkdownCompletionItemProvider(), ' ', ','); + vscode.languages.registerFoldingRangeProvider('r', new rmarkdown.RChunkFoldingProvider()); -// } + // register (session) hover and completion providers + vscode.languages.registerHoverProvider(['r', 'rmd'], new completions.HoverProvider()); + vscode.languages.registerHoverProvider(['r', 'rmd'], new completions.HelpLinkHoverProvider()); + vscode.languages.registerCompletionItemProvider(['r', 'rmd'], new completions.StaticCompletionItemProvider(), '@'); + + // deploy liveshare listener + await rShare.initLiveShare(context); + + // register task provider + const taskProvider = new RTaskProvider(); + vscode.tasks.registerTaskProvider(taskProvider.type, taskProvider); + + // deploy session watcher (if configured by user) + if (enableSessionWatcher) { + if (!rShare.isGuestSession) { + console.info('Initialize session watcher'); + void session.deploySessionWatcher(context.extensionPath); + + // create status bar item that contains info about the session watcher + console.info('Create sessionStatusBarItem'); + sessionStatusBarItem = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Right, 1000); + sessionStatusBarItem.command = 'r.attachActive'; + sessionStatusBarItem.text = 'R: (not attached)'; + sessionStatusBarItem.tooltip = 'Click to attach active terminal.'; + sessionStatusBarItem.show(); + context.subscriptions.push(sessionStatusBarItem); + void session.startRequestWatcher(sessionStatusBarItem); + } + + // track active text editor + rstudioapi.trackLastActiveTextEditor(vscode.window.activeTextEditor); + vscode.window.onDidChangeActiveTextEditor(rstudioapi.trackLastActiveTextEditor); + + // register the R Workspace tree view + // creates a custom context value for the workspace view + // only shows view when session watcher is enabled + rWorkspace = new workspaceViewer.WorkspaceDataProvider(); + + // if session watcher is active, register dyamic completion provider + const liveTriggerCharacters = ['', '[', '(', ',', '$', '@', '"', '\'']; + vscode.languages.registerCompletionItemProvider(['r', 'rmd'], new completions.LiveCompletionItemProvider(), ...liveTriggerCharacters); + } + + void vscode.commands.executeCommand('setContext', 'r.WorkspaceViewer:show', enableSessionWatcher); + + return rExtension; +} diff --git a/src/helpViewer/cran.ts b/src/helpViewer/cran.ts new file mode 100644 index 000000000..ea822e6b4 --- /dev/null +++ b/src/helpViewer/cran.ts @@ -0,0 +1,93 @@ + +import * as cheerio from 'cheerio'; +import { Package} from './packages'; +import fetch from 'node-fetch'; + +type ParseFunction = (html: string, baseUrl: string) => Package[]; + +export async function getPackagesFromCran(cranUrl: string): Promise { + const cranSites: {url: string, parseFunction: ParseFunction}[] = [ + // NOTE: Not working any more + // { + // url: new URL('stats/descriptions', cranUrl).toString(), + // parseFunction: parseCranJson + // }, + { + url: new URL('web/packages/available_packages_by_date.html', cranUrl).toString(), + parseFunction: parseCranTable + }, + { + url: new URL('src/contrib/PACKAGES', cranUrl).toString(), + parseFunction: parseCranPackagesFile + } + ]; + let packages: Package[] = []; + for(const site of cranSites){ + try{ + // fetch html + process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0'; // seems to fail otherwise? + const res = await fetch(site.url); + const html = await (res).text(); + + // parse html + packages = site.parseFunction(html, site.url); + } catch(e) { + // These errors are expected, if the repo does not serve a specific URL + } finally { + // make sure to use safe https again + process.env.NODE_TLS_REJECT_UNAUTHORIZED = '1'; + } + + // break if successfully fetched & parsed + if(packages?.length){ + break; + } + } + return packages; +} + +function parseCranPackagesFile(html: string): Package[] { + const packageNames = html.match(/^Package: .*$/gm)?.map(s => s.replace(/^Package: /, '')) || []; + const packages: Package[] = packageNames.map(s => ({ + name: s, + description: '', + isCran: true + })); + return packages; +} + + + +function parseCranTable(html: string, baseUrl: string): Package[] { + if(!html){ + return []; + } + const $ = cheerio.load(html); + const tables = $('table'); + const ret: Package[] = []; + + // loop over all tables on document and each row as one index entry + // assumes that the provided html is from a valid index file + tables.each((tableIndex, table) => { + const rows = $('tr', table); + rows.each((rowIndex, row) => { + if (rowIndex === 0) {return;} // Skip the header row + const date = $(row).find('td:nth-child(1)').text().trim(); + const href = $(row).find('td:nth-child(2) a').attr('href'); + const url = href ? new URL(href, baseUrl).toString() : undefined; + const name = $(row).find('td:nth-child(2) span').text().trim(); + const title = $(row).find('td:nth-child(3)').text().trim(); + ret.push({ + date: date, + name: name, + href: url, + description: title, + isCran: true + }); + }); + }); + + const retSorted = ret.sort((a, b) => a.name.localeCompare(b.name)); + + return retSorted; +} diff --git a/src/helpViewer/helpPreviewer.ts b/src/helpViewer/helpPreviewer.ts new file mode 100644 index 000000000..2b6d61297 --- /dev/null +++ b/src/helpViewer/helpPreviewer.ts @@ -0,0 +1,529 @@ + +import * as path from 'path'; +import * as fs from 'fs'; +import * as vscode from 'vscode'; +import * as rHelp from './index'; +import * as ejs from 'ejs'; +import { isDirSafe, isFileSafe, readFileSyncSafe, config, spawnAsync } from '../util'; +import { Topic, TopicType } from './packages'; + + +// Information from the corresponding fields in DESCRIPION +interface LocalPackageInfo { + name?: string; + version?: string; + title?: string; +} + +// Used to create a preview of 00Index.html +interface IndexEjsTopic { + name: string; + href: string; + title: string; +} +interface IndexEjsData { + packageTitle: string; + packageName: string; + packageVersion: string; + topics: IndexEjsTopic[]; +} + +interface RdAlias { + // path of .Rd file + filepath: string; + // (unique) name of the topic + name: string; + // title of the topic + title?: string; + // (possibly multiple) aliases of the topic + aliases: string[]; +} + +interface AliasExtra extends rHelp.Alias { + // title of the topic + title?: string; + // filepath of the corresponding .Rd file + rdPath?: string; +} + +export interface RHelpPreviewerOptions { + // path of the R executable + rPath: string; + // listener to notify when package-files change + previewListener?: (previewer: RLocalHelpPreviewer) => void; + // path to .ejs file to be used as 00Index.html in previewed packages + indexTemplatePath: string; + // path of the script used to convert .Rd to html + rdToHtmlScriptFile: string +} + +export function makePreviewerList(options: RHelpPreviewerOptions): RLocalHelpPreviewer[] { + const subDirs = config().get('helpPanel.previewLocalPackages', []); + const workspaces = vscode.workspace.workspaceFolders || []; + const ret: RLocalHelpPreviewer[] = []; + let previewCounter = 1; + for (const workspace of workspaces) { + for(const subDir of subDirs){ + const dir = vscode.Uri.joinPath(workspace.uri, subDir); + const dirPath = dir.fsPath; + const tmpPreviewer = new RLocalHelpPreviewer(options, dirPath, previewCounter); + if(tmpPreviewer.isPackageDir){ + ret.push(tmpPreviewer); + previewCounter = previewCounter + 1; + } else{ + tmpPreviewer.dispose(); + } + } + } + return ret; +} + +const DUMMY_TOPIC_TITLE = ''; +const DUMMY_TOPIC_VERSION = '?.?.?'; +const DUMMY_PACKAGE_TITLE = ''; + +export class RLocalHelpPreviewer { + + public readonly packageDir: string; + private readonly descriptionPath: string; + private readonly manDir: string; + private readonly rPath: string; + private readonly indexTemplate: string; + private callPreviewListener: () => void; + + // path of the script used to convert .Rd to html + private readonly rdToHtmlScriptFile: string; + + public isPackageDir: boolean = false; + public isDisposed: boolean = false; + + private readonly fileWatchers: fs.FSWatcher[] = []; + private cachedRdAliases?: Map; + private cachedPackageInfo?: LocalPackageInfo; + + private readonly dummyPackageName: string; + + constructor(options: RHelpPreviewerOptions, packageDir: string, unnamedId: number = 1) { + this.packageDir = packageDir; + this.descriptionPath = path.join(this.packageDir, 'DESCRIPTION'); + this.manDir = path.join(this.packageDir, 'man'); + this.rPath = options.rPath; + this.callPreviewListener = () => options.previewListener?.(this); + this.isPackageDir = this.watchFiles(); + this.dummyPackageName = `UnnamedPackage${unnamedId}`; + this.indexTemplate = fs.readFileSync(options.indexTemplatePath, 'utf-8'); + this.rdToHtmlScriptFile = options.rdToHtmlScriptFile; + } + + public refresh(): void { + // nothing to do, since file watchers keep everything updated + } + public dispose(callListener = false): void { + this.isPackageDir = false; + while(this.fileWatchers.length){ + this.fileWatchers.pop()?.close(); + } + this.isDisposed = true; + if(callListener){ + this.callPreviewListener(); + } + } + + // Is only called once as part of the constructor + // It is expected that this instance will be disposed if this method returns false + private watchFiles(): boolean { + // Only watch any files, if both man dir and DESCRIPTION exist + if(!isFileSafe(this.descriptionPath) || !isDirSafe(this.manDir)){ + return false; + } + + // Prepare listeners + const errorListener = () => { + console.log(`Disposing previewer for pkgDir: ${this.packageDir}`); + void this.dispose(true); + }; + const descriptionListener: fs.WatchListener = () => { + this.cachedPackageInfo = undefined; + this.callPreviewListener(); + }; + const manDirListener: fs.WatchListener = (event: fs.WatchEventType, filename: string | null) => { + if(this.isDisposed){ + return; + } + if(!isDirSafe(this.manDir)){ + this.dispose(true); + return; + } + if (filename === null) { + return; + } + const fullPath = path.join(this.manDir, filename); + // The cache is only initialized when it is needed for the first time: + if(this.cachedRdAliases){ + const rdAlias = getRdAlias(fullPath); + if(rdAlias){ + this.cachedRdAliases.set(fullPath, rdAlias); + } else{ + this.cachedRdAliases.delete(fullPath); + } + } + this.callPreviewListener(); + }; + + // Watch man dir/DESCRIPTION + const descWatcher = fs.watch(this.descriptionPath, {encoding: 'utf-8'}); + descWatcher.on('change', descriptionListener); + descWatcher.on('error', errorListener); + this.fileWatchers.push(descWatcher); + + const manDirWatcher = fs.watch(this.manDir, {encoding: 'utf-8'}); + manDirWatcher.on('change', manDirListener); + manDirWatcher.on('error', errorListener); + this.fileWatchers.push(manDirWatcher); + + return true; + } + + public getPackageInfo(): LocalPackageInfo | undefined { + if(this.cachedPackageInfo){ + return this.cachedPackageInfo; + } + const desc = readFileSyncSafe(this.descriptionPath, 'utf-8'); + if(!desc){ + return undefined; + } + const packageInfo: LocalPackageInfo = {}; + const nameMatch = /^Package:\s*(.*?)\s*$/m.exec(desc); + packageInfo.name = nameMatch?.[1]; + const versionMatch = /^Version:\s*(.*?)\s*$/m.exec(desc); + packageInfo.version = versionMatch?.[1]; + const titleMatch = /^Title:\s*(.*?)\s*$/m.exec(desc); + packageInfo.title = titleMatch?.[1]; + this.cachedPackageInfo = packageInfo; + return packageInfo; + } + + public getPackageName(safe?: boolean): string { + const packageName = this.getPackageInfo()?.name; + if(!packageName || (safe && !isValidPackageName(packageName))){ + return this.dummyPackageName; + } + return packageName; + } + + // Methods that imitate the HelpProvider + public async getHelpFileFromRequestPath(requestPath: string): Promise { + if(this.isDisposed){ + return undefined; + } + const {pkg, topic} = parseRequestPath(requestPath); + if(!topic || !pkg || (pkg !== this.getPackageName() && pkg !== this.getPackageName(true))){ + return undefined; + } + if(topic === '00Index'){ + return this.getHelpForIndex(requestPath); + } + if(topic === 'DESCRIPTION'){ + return this.getHelpForDescription(requestPath); + } + return await this.getHelpForTopic(topic, requestPath); + } + + private async getHelpForTopic(topic: string, requestPath: string): Promise { + // Make sure the topic has a valid .Rd file + const rdFileName = this.getRdPathForTopic(topic); + if(!rdFileName || !isFileSafe(rdFileName)){ + return undefined; + } + + // Convert .Rd to HTML + const args = [ + '--silent', + '--no-echo', + '--no-save', + '--no-restore', + '-f', + this.rdToHtmlScriptFile, + '--args', + rdFileName, + this.getPackageName(true), + this.getPackageInfo()?.version || DUMMY_TOPIC_VERSION, + this.packageDir + ]; + const spawnRet = await spawnAsync(this.rPath, args); + if(spawnRet.status){ + // The user expects this to work, so we show a warning if it doesn't: + const msg = `Failed to convert .Rd file ${rdFileName} (status: ${spawnRet.status}): ${spawnRet.stderr}`; + void vscode.window.showWarningMessage(msg); + console.log(msg); + console.log(spawnRet.stderr); + console.log(spawnRet.error); + return undefined; + } + + // Prepare HelpFile + const helpFile: rHelp.HelpFile = { + html: spawnRet.stdout, + requestPath: requestPath, + isPreview: true, + rdPath: rdFileName, + packageDir: this.packageDir + }; + + // Add path of .R containing Roxygen documentation + const rdTxt = fs.readFileSync(rdFileName, 'utf-8').replaceAll(/\r/g, ''); + const localRPaths = extractRPaths(rdTxt); + helpFile.rPaths = localRPaths?.map(p => path.join(this.packageDir, p)); + return helpFile; + } + + private getRdPathForTopic(topic: string): string | undefined { + const rdAliases = this.getRdAliases(); + for(const [fullPath, rdAlias] of rdAliases){ + if(rdAlias.aliases.includes(topic)){ + return fullPath; + } + } + return undefined; + } + + private getHelpForDescription(requestPath: string): rHelp.HelpFile | undefined { + const desc = readFileSyncSafe(this.descriptionPath); + if(!desc){ + return undefined; + } + // might need to be handled differently if the handling in index.ts changes: + const helpFile: rHelp.HelpFile = { + html: desc, + requestPath: requestPath, + isPreview: true, + rdPath: this.descriptionPath, + packageDir: this.packageDir + }; + return helpFile; + } + + private getHelpForIndex(requestPath: string): rHelp.HelpFile | undefined { + const html = this.makeIndexHtml(); + if(!html){ + return undefined; + } + const helpFile: rHelp.HelpFile = { + html: html, + requestPath: requestPath, + isPreview: true, + isIndex: true, + rdPath: undefined, + packageDir: this.packageDir + }; + + return helpFile; + } + + private makeIndexHtml(): string | undefined { + const pkgInfo = this.getPackageInfo(); + if(!pkgInfo){ + return undefined; + } + const aliases = this.getAliases(); + const topics = aliases.map(alias => ({ + name: alias.alias, + title: alias.title || DUMMY_TOPIC_TITLE, + href: `${alias.name}.html` + })); + const ejsData: IndexEjsData = { + packageName: pkgInfo.name || this.dummyPackageName, + packageTitle: pkgInfo.title || DUMMY_PACKAGE_TITLE, + packageVersion: pkgInfo.version || DUMMY_TOPIC_VERSION, + topics: topics + }; + const html = ejs.render(this.indexTemplate, ejsData); + return html; + } + + // Method that imitates the AliasProvider + public getAliases(): AliasExtra[] { + const rdAliases = this.getRdAliases().values(); + const pkgName = this.getPackageName(); + const aliases = [...rdAliases].flatMap(rdAlias => rdAliasToAliases(rdAlias, pkgName)); + return aliases; + } + + private getRdAliases(): Map { + // Return cache if exists (is updated by file watchers) + if(this.cachedRdAliases){ + return this.cachedRdAliases; + } + // Else, initialize and populate cache + const rdAliases = getRdAliases(this.manDir); + this.cachedRdAliases = new Map(); + for (const rdAlias of rdAliases) { + this.cachedRdAliases.set(rdAlias.filepath, rdAlias); + } + return this.cachedRdAliases; + } + + // Method that imitates the PackageManager + public getTreeViewTopics(summarize: boolean = false): Topic[] { + const pkgName = this.getPackageName(true); + let topics: Topic[]; + if(summarize){ + const rdAliases = getRdAliases(this.manDir); + topics = rdAliases.map(rdAlias => rdAliasToTreeViewTopic(rdAlias, pkgName)); + } else{ + const aliases = this.getAliases(); + topics = aliases.map(alias => { + const helpPath = `/library/${pkgName}/html/${alias.alias}.html`; + const topic: Topic = { + name: alias.alias, + description: alias.title || DUMMY_TOPIC_TITLE, + type: TopicType.NORMAL, + helpPath: helpPath + }; + return topic; + }); + } + + const descriptionTopic: Topic = { + name: 'DESCRIPTION', + description: '', + helpPath: `/library/${pkgName}/DESCRIPTION`, + type: TopicType.META + }; + + const indexTopic: Topic = { + name: 'Index', + description: '', + helpPath: `/library/${pkgName}/html/00Index.html`, + type: TopicType.INDEX + }; + topics.unshift(indexTopic, descriptionTopic); + return topics; + } + +} + + +// Helper function to extract the names of R files referenced in an .Rd file +function extractRPaths(rdTxt: string): string[] | undefined { + // Find the commented lines at the begining of the document + const lines = rdTxt.replaceAll(/\r/g, '').split('\n'); + const firstRealLine = lines.findIndex(lines => !lines.startsWith('% ')); + if(firstRealLine >= 0){ + lines.splice(firstRealLine); + } + + // Join lines that were split (these start with "% ") + const CONTINUED_LINE_START = '% '; + const longLines = []; + for(const line of lines){ + if(line.startsWith(CONTINUED_LINE_START) && longLines.length){ + longLines[longLines.length - 1] += ' ' + line.substring(CONTINUED_LINE_START.length); + } else{ + longLines.push(line); + } + } + + // Find the line that references R files + for(const line of longLines){ + const rFileMatch = line.match(/^% Please edit documentation in (.*)$/); + if(rFileMatch){ + const localRPaths = rFileMatch?.[1].split(',').map(s => s.trim()); + return localRPaths; + } + } + return undefined; +} + + +// Helper function to parse a request path +// Accepts e.g. paths of the forms +// - library/PKG/html/TOPIC +// - library/PKG/help/TOPIC +// - library/PKG/help/TOPIC.html/.... +// - library/PKG/TOPIC +function parseRequestPath(requestPath: string): { + pkg?: string, + topic?: string +} { + const re = /^\/?library\/([^/]*)\/(?:html|help)?\/?([^/]*?)(?:\.html.*)?$/; + const m = re.exec(requestPath); + return { + pkg: m?.[1], + topic: m?.[2].replace(/^dot-/, '.') + }; +} + +// Convert a single rdAlias to a (summarized) tree view topic entry +function rdAliasToTreeViewTopic(rdAlias: RdAlias, pkgName: string): Topic { + const ret: Topic = { + helpPath: `/library/${pkgName}/html/${rdAlias.name}.html`, + name: rdAlias.title || rdAlias.name || DUMMY_TOPIC_TITLE, + type: TopicType.NORMAL, + aliases: rdAlias.aliases, + description: rdAlias.title || DUMMY_TOPIC_TITLE + }; + return ret; +} + + +// Check if a package name is valid +function isValidPackageName(pkgName: string): boolean { + // regex to chekc pkgName (length >=2 implied): + // /^[beging with letter][letters,numbers,dot][not end with .]$/ + const re = /^[a-zA-Z][a-zA-Z0-9.]*[a-zA-Z0-9]$/; + return !!re.exec(pkgName); +} + + +// Helper functions to read/convert rdAliases and aliases + +function rdAliasToAliases(rdAlias: RdAlias, pkgName: string): AliasExtra[] { + return rdAlias.aliases.map(alias => ({ + package: pkgName, + name: rdAlias.name, + alias: alias, + title: rdAlias.title, + rdPath: rdAlias.filepath + })); +} + +function getRdAliases(manDir: string): RdAlias[] { + const manFiles = fs.readdirSync(manDir) || []; + const aliases: RdAlias[] = []; + manFiles.forEach(filename => { + if(!filename.match(/\.[Rr][Dd]$/)){ + return; + } + const fullPath = path.join(manDir, filename); + const rdAlias = getRdAlias(fullPath); + if(rdAlias){ + aliases.push(rdAlias); + } + }); + return aliases; +} + +function getRdAlias(rdFile: string): RdAlias | undefined { + const txt = readFileSyncSafe(rdFile, 'utf-8'); + if(!txt){ + return undefined; + } + const nameMatch = txt.match(/\\name\{(.*)\}/); + const name = nameMatch?.[1]; + if(!name){ + return undefined; + } + const ret: RdAlias = { + filepath: rdFile, + name: name, + aliases: [] + }; + const titleMatch = txt.match(/\\title\{(.*)\}/); + ret.title = titleMatch?.[1]; + const aliasMatches = txt.matchAll(/\\alias\{(.*)\}/g); + for (const m of aliasMatches) { + ret.aliases.push(m[1]); + } + return ret; +} diff --git a/src/helpViewer/helpProvider.ts b/src/helpViewer/helpProvider.ts new file mode 100644 index 000000000..422126217 --- /dev/null +++ b/src/helpViewer/helpProvider.ts @@ -0,0 +1,286 @@ +import { Memento, window } from 'vscode'; +import * as nodeFetch from 'node-fetch'; +import * as cp from 'child_process'; + +import * as rHelp from '.'; +import { extensionContext } from '../extension'; +import { catchAsError, config, DisposableProcess, getRLibPaths, spawn, spawnAsync } from '../util'; + +export interface RHelpProviderOptions { + // path of the R executable + rPath: string; + // directory in which to launch R processes + cwd?: string; + // listener to notify when new packages are installed + pkgListener?: () => void; +} + +type ChildProcessWithPort = DisposableProcess & { + port?: number | Promise; +}; + +// Class to forward help requests to a backgorund R instance that is running a help server +export class HelpProvider { + private cp: ChildProcessWithPort; + private readonly rPath: string; + private readonly cwd?: string; + private readonly pkgListener?: () => void; + + public constructor(options: RHelpProviderOptions){ + this.rPath = options.rPath || 'R'; + this.cwd = options.cwd; + this.pkgListener = options.pkgListener; + this.cp = this.launchRHelpServer(); + } + + public async refresh(): Promise { + this.cp.dispose(); + this.cp = this.launchRHelpServer(); + await this.cp.port; + } + + public launchRHelpServer(): ChildProcessWithPort{ + const lim = '---vsc---'; + const portRegex = new RegExp(`.*${lim}(.*)${lim}.*`, 'ms'); + + const newPackageRegex = new RegExp('NEW_PACKAGES'); + + // starts the background help server and waits forever to keep the R process running + const scriptPath = extensionContext.asAbsolutePath('R/help/helpServer.R'); + const args = [ + '--silent', + '--no-echo', + '--no-save', + '--no-restore', + '-e', + 'base::source(base::commandArgs(TRUE))', + '--args', + scriptPath + ]; + const cpOptions = { + cwd: this.cwd, + env: { + ...process.env, + VSCR_LIB_PATHS: getRLibPaths(), + VSCR_LIM: lim, + VSCR_USE_RENV_LIB_PATH: config().get('useRenvLibPath') ? 'TRUE' : 'FALSE' + }, + }; + + const childProcess: ChildProcessWithPort = spawn(this.rPath, args, cpOptions); + + let str = ''; + // promise containing the port number of the process (or 0) + const portPromise = new Promise((resolve) => { + childProcess.stdout?.on('data', (data) => { + try{ + // eslint-disable-next-line + str += data.toString(); + } catch(e){ + resolve(0); + } + if(portRegex.exec(str)){ + resolve(Number(str.replace(portRegex, '$1'))); + str = str.replace(portRegex, ''); + } + if(newPackageRegex.exec(str)){ + this.pkgListener?.(); + str = str.replace(newPackageRegex, ''); + } + }); + childProcess.on('close', () => { + resolve(0); + }); + }); + + const exitHandler = () => { + childProcess.port = 0; + }; + childProcess.on('exit', exitHandler); + childProcess.on('error', exitHandler); + + // await and store port number + childProcess.port = portPromise; + + // is returned as a promise if not called with "await": + return childProcess; + } + + public async getHelpFileFromRequestPath(requestPath: string): Promise { + + const port = await this.cp?.port; + if(!port || typeof port !== 'number'){ + return undefined; + } + + // remove leading '/' + while(requestPath.startsWith('/')){ + requestPath = requestPath.slice(1); + } + + // forward request to R instance + const url = `http://localhost:${port}/${requestPath}`; + const rep = await nodeFetch.default(url); + if(rep.status !== 200){ + return undefined; + } + const html = await rep.text(); + + // read "corrected" request path, that was forwarded to + const requestPath1 = rep.url.replace(/^http:\/\/localhost:[0-9]*\//, ''); + + // return help file + const ret: rHelp.HelpFile = { + requestPath: requestPath1, + html: html, + isRealFile: false, + url: url + }; + return ret; + } + + dispose(): void { + this.cp.dispose(); + } +} + + +export interface AliasProviderArgs { + // R path, must be vanilla R + rPath: string; + // cwd + cwd?: string; + // getAliases.R + rScriptFile: string; + + persistentState: Memento; +} + +interface PackageAliases { + package?: string; + libPath?: string; + aliasFile?: string; + aliases?: { + [key: string]: string; + }, + error?: string +} +interface AllPackageAliases { + [key: string]: PackageAliases +} + +// Implements the aliasProvider required by the help panel +export class AliasProvider { + + private readonly rPath: string; + private readonly cwd?: string; + private readonly rScriptFile: string; + private aliases?: undefined | rHelp.Alias[]; + private readonly persistentState?: Memento; + + constructor(args: AliasProviderArgs){ + this.rPath = args.rPath; + this.cwd = args.cwd; + this.rScriptFile = args.rScriptFile; + this.persistentState = args.persistentState; + } + + // delete stored aliases, will be generated on next request + public async refresh(): Promise { + this.aliases = undefined; + await this.persistentState?.update('r.helpPanel.cachedAliases', undefined); + await this.makeAllAliases(); + } + + // get a list of all aliases + public async getAllAliases(): Promise { + // try this.aliases: + if(this.aliases){ + return this.aliases; + } + + // try cached aliases: + const cachedAliases = this.persistentState?.get('r.helpPanel.cachedAliases'); + if(cachedAliases){ + this.aliases = cachedAliases; + return cachedAliases; + } + + // try to make new aliases (returns undefined if unsuccessful): + const newAliases = await this.makeAllAliases(); + this.aliases = newAliases; + await this.persistentState?.update('r.helpPanel.cachedAliases', newAliases); + return newAliases; + } + + // converts aliases grouped by package to a flat list of aliases + private async makeAllAliases(): Promise { + // get aliases from R (nested format) + const allPackageAliases = await this.getAliasesFromR(); + if(!allPackageAliases){ + return undefined; + } + + // flatten aliases into one list: + const allAliases: rHelp.Alias[] = []; + for (const pkg in allPackageAliases) { + const item = allPackageAliases[pkg]; + const pkgName = item.package || pkg; + + if (item.error) { + void window.showErrorMessage(`An error occurred while reading the aliases file for package ${pkgName}: ${item.error}. The package files may be corrupted. Try reinstalling the package.`); + continue; + } + + const pkgAliases = item.aliases || {}; + for(const fncName in pkgAliases){ + allAliases.push({ + name: pkgAliases[fncName], + alias: fncName, + package: pkgName + }); + } + } + return allAliases; + } + + // call R script `getAliases.R` and parse the output + private async getAliasesFromR(): Promise { + const lim = '---vsc---'; + const options: cp.CommonOptions = { + cwd: this.cwd, + env: { + ...process.env, + VSCR_LIB_PATHS: getRLibPaths(), + VSCR_LIM: lim, + VSCR_USE_RENV_LIB_PATH: config().get('useRenvLibPath') ? 'TRUE' : 'FALSE' + } + }; + + const args = [ + '--silent', + '--no-echo', + '--no-save', + '--no-restore', + '-f', + this.rScriptFile + ]; + + try { + const result = await spawnAsync(this.rPath, args, options); + if (result.status !== 0) { + throw result.error || new Error(result.stderr); + } + const re = new RegExp(`${lim}(.*)${lim}`, 'ms'); + const match = re.exec(result.stdout); + if (match?.length !== 2) { + throw new Error('Could not parse R output.'); + } + const json = match[1]; + return JSON.parse(json) || {}; + } catch (e) { + console.log(e); + void window.showErrorMessage(catchAsError(e).message); + } + } +} diff --git a/src/helpViewer/index.ts b/src/helpViewer/index.ts new file mode 100644 index 000000000..3b0f62770 --- /dev/null +++ b/src/helpViewer/index.ts @@ -0,0 +1,778 @@ +/* eslint-disable @typescript-eslint/no-unsafe-argument */ +/* eslint-disable @typescript-eslint/no-explicit-any */ + +import * as vscode from 'vscode'; +import * as cheerio from 'cheerio'; +import * as hljs from 'highlight.js'; + +import * as api from '../api'; + +import { + config, + getRpath, + doWithProgress, + DummyMemento, + getRPathConfigEntry, + escapeHtml, + makeWebviewCommandUriString, + uniqueEntries, + isFileSafe, +} from '../util'; +import {HelpPanel} from './panel'; +import {HelpProvider, AliasProvider} from './helpProvider'; +import {HelpTreeWrapper} from './treeView'; +import {PackageManager} from './packages'; +import {isGuestSession, rGuestService} from '../liveShare'; +import { makePreviewerList, RHelpPreviewerOptions, RLocalHelpPreviewer } from './helpPreviewer'; + +export type CodeClickAction = 'Ignore' | 'Copy' | 'Run'; +export interface CodeClickConfig { + 'Click': CodeClickAction, + 'Ctrl+Click': CodeClickAction, + 'Shift+Click': CodeClickAction, +} +const CODE_CLICKS: (keyof CodeClickConfig)[] = ['Click', 'Ctrl+Click', 'Shift+Click']; +export const codeClickConfigDefault = { + 'Click': 'Copy', + 'Ctrl+Click': 'Run', + 'Shift+Click': 'Ignore', +}; + +// Initialization function that is called once when activating the extension +export async function initializeHelp( + context: vscode.ExtensionContext, + rExtension: api.RExtension, +): Promise { + // set context value to indicate that the help related tree-view should be shown + void vscode.commands.executeCommand('setContext', 'r.helpViewer.show', true); + + // get the "vanilla" R path from config + const rPath = await getRpath(); + if(!rPath){ + return undefined; + } + + // get the current working directory from vscode + const cwd = vscode.workspace.workspaceFolders?.length + ? vscode.workspace.workspaceFolders[0].uri.fsPath + : undefined; + + // get the Memento for storing cached help files (or create a dummy for this session) + const cacheConfig = config().get<'None' | 'Workspace' | 'Global'>( + 'helpPanel.cacheIndexFiles', + ); + const persistentState = + cacheConfig === 'Workspace' + ? context.workspaceState + : cacheConfig === 'Global' + ? context.globalState + : new DummyMemento(); + + // Gather options used in r help related files + const rHelpOptions: HelpOptions = { + webviewScriptPath: context.asAbsolutePath('./html/help/script.js'), + webviewStylePath: context.asAbsolutePath('./html/help/theme.css'), + rScriptFile: context.asAbsolutePath('./R/help/getAliases.R'), + indexTemplatePath: context.asAbsolutePath('./html/help/00Index.ejs'), + rdToHtmlScriptFile: context.asAbsolutePath('./R/help/rdToHtml.R'), + rPath: rPath, + cwd: cwd, + persistentState: persistentState, + }; + + let rHelp: RHelp | undefined = undefined; + + try { + rHelp = new RHelp(rHelpOptions); + } catch (e) { + console.log('Error while launching R Help:', e); + void vscode.window.showErrorMessage(`Help Panel not available`); + } + + rExtension.helpPanel = rHelp; + + if (rHelp) { + // make sure R child processes etc. are terminated when extension closes + context.subscriptions.push(rHelp); + + // register help related commands + + context.subscriptions.push( + vscode.commands.registerCommand('r.showHelp', () => + rHelp?.treeViewWrapper.helpViewProvider.rootItem.showQuickPick(), + ), + vscode.commands.registerCommand('r.helpPanel.back', () => + rHelp?.getActiveHelpPanel(false)?.goBack(), + ), + vscode.commands.registerCommand('r.helpPanel.forward', () => + rHelp?.getActiveHelpPanel(false)?.goForward(), + ), + vscode.commands.registerCommand('r.helpPanel.openExternal', () => + rHelp?.getActiveHelpPanel(false)?.openInExternalBrowser(), + ), + vscode.commands.registerCommand( + 'r.helpPanel.openForSelection', + (preserveFocus: boolean = false) => + rHelp?.openHelpForSelection(!!preserveFocus), + ), + vscode.commands.registerCommand( + 'r.helpPanel.openForPath', + (path?: string) => { + if (path) { + void rHelp?.showHelpForPath(path); + } + }, + ), + vscode.commands.registerCommand( + 'r.helpPanel.openFileByPath', + async (filepath: string, warn?: boolean) => { + if(isFileSafe(filepath)){ + const uri = vscode.Uri.file(filepath); + await vscode.window.showTextDocument(uri); + } else if(warn){ + await vscode.window.showWarningMessage(`The file does not exist: ${filepath}`); + } + } + ) + ); + + vscode.window.registerWebviewPanelSerializer('rhelp', rHelp); + } + + return rHelp; +} + +// Internal representation of a help file +export interface HelpFile { + // content of the file + html: string + // whether the html has been modified already (syntax highlighting etc.) + isModified?: boolean + // original content of the file (only used if isModified===true) + html0?: string + // flag indicating whether the original file content is html + isHtml?: boolean + // path as used by help server. Uses '/' as separator! + requestPath: string + // hash-part of the requested URL + hash?: string + // if the file is a real file + isRealFile?: boolean + // can be set to true to indicate that the file is a (virtual) 00Index.html file + isIndex?: boolean + // can be used to scroll the document to a certain position when loading + // useful to remember scroll position when going back/forward + scrollY?: number + // used to open the file in an external browser + url?: string + // indicates that this is a preview generated from a .Rd file + isPreview?: boolean; + // the .Rd file that this is based on (if it is a preview) + rdPath?: string; + // if available, the .R file from which the documentation is generated + rPaths?: string[]; + // if available, the directory of the previewed R package + packageDir?: string; +} + +// Internal representation of an "Alias" +export interface Alias { + // main name of a help topic + name: string + // one of possibly many aliases of the same help topic + alias: string + // name of the package the alias is from + package: string +} + +// Options to be specified when creating a new rHelp instance (used only once per session) +export interface HelpOptions { + /* Local path of script.js, used to send messages to vs code */ + webviewScriptPath: string + /* Local path of theme.css, used to actually format the highlighted syntax */ + webviewStylePath: string + // path of the R executable + rPath: string + // directory in which to launch R processes + cwd?: string + // path of getAliases.R + rScriptFile: string + // path of the script used to convert .Rd to html + rdToHtmlScriptFile: string + // persistent state, either global or workspace specific + persistentState: vscode.Memento + // used by some helper classes: + rHelp?: RHelp + // path to .ejs file to be used as 00Index.html in previewed packages + indexTemplatePath: string; +} + +// The name api.HelpPanel is a bit misleading +// This class manages all R-help and R-packages related functions +export class RHelp implements api.HelpPanel, vscode.WebviewPanelSerializer +{ + // Path of a vanilla R installation + readonly rPath: string + + // If applicable, the currently opened wd. + // Used to read the correct .Rprofile when launching R + readonly cwd?: string + + // Provides the content of help pages: + readonly helpProvider: HelpProvider + + // Provides a list of aliases: + readonly aliasProvider: AliasProvider + + // Provides previews of local help pages: + readonly previewProviders: RLocalHelpPreviewer[] + + // Show/Install/Remove packages: + readonly packageManager: PackageManager + + // The tree view that shows available packages and help topics + readonly treeViewWrapper: HelpTreeWrapper + + // the webview panel(s) where the help is shown + public readonly helpPanels: HelpPanel[] = [] + + // locations on disk, only changed on construction + readonly webviewScriptFile: vscode.Uri // the javascript added to help pages + readonly webviewStyleFile: vscode.Uri // the css file applied to help pages + + // cache for modified help files (syntax highlighting etc.) + private cachedHelpFiles: Map = new Map< + string, + HelpFile | undefined + >() + + // The options used when creating this instance + private helpPanelOptions: HelpOptions + private helpPreviewerOptions: RHelpPreviewerOptions + + constructor(options: HelpOptions) { + this.rPath = options.rPath; + this.webviewScriptFile = vscode.Uri.file(options.webviewScriptPath); + this.webviewStyleFile = vscode.Uri.file(options.webviewStylePath); + const pkgListener = () => { + console.log('Restarting Help Server...'); + void this.refresh(true); + }; + this.helpProvider = new HelpProvider({ + ...options, + pkgListener: pkgListener, + }); + this.aliasProvider = new AliasProvider(options); + const previewListener = (previewer: RLocalHelpPreviewer) => { + console.log(`Refreshing R Help preview: ${previewer.packageDir}`); + void this.refreshPreviewer(previewer); + }; + this.helpPreviewerOptions = { + indexTemplatePath: options.indexTemplatePath, + rdToHtmlScriptFile: options.rdToHtmlScriptFile, + rPath: this.rPath, + previewListener: previewListener + }; + this.previewProviders = makePreviewerList(this.helpPreviewerOptions); + this.packageManager = new PackageManager({...options, rHelp: this}); + this.treeViewWrapper = new HelpTreeWrapper(this); + this.helpPanelOptions = options; + } + + async deserializeWebviewPanel( + webviewPanel: vscode.WebviewPanel, + path: string, + ): Promise { + const panel = this.makeNewHelpPanel(webviewPanel); + await this.showHelpForPath(path, undefined, true, panel); + return; + } + + // used to close files, stop servers etc. + public dispose(): void { + const children = [ + this.helpProvider, + this.aliasProvider, + this.packageManager, + this.treeViewWrapper, + ...this.helpPanels, + ...this.previewProviders + ]; + for (const child of children) { + if ( + child && + 'dispose' in child && + typeof child.dispose === 'function' + ) { + try { + child.dispose(); + } catch (e) {} + } + } + } + + // refresh cached help info + public async refresh(refreshTreeView: boolean = false): Promise { + this.cachedHelpFiles.clear(); + await this.helpProvider?.refresh?.(); + await this.aliasProvider?.refresh?.(); + await this.packageManager?.refresh?.(); + + // completely replace previewers + while(this.previewProviders.length){ + this.previewProviders.pop()?.dispose(); + } + this.previewProviders.push(...makePreviewerList(this.helpPreviewerOptions)); + + // refresh helpPanels + for (const panel of this.helpPanels) { + await panel.refresh(); + } + + // refresh tree view + if (refreshTreeView) { + this.treeViewWrapper.refreshPackageRootNode(); + this.treeViewWrapper.refreshRootNode(); + } + return true; + } + + // refresh only a certain preview: + public refreshPreviewer(previewer: RLocalHelpPreviewer): void { + if(previewer.isDisposed){ + const ind = this.previewProviders.indexOf(previewer); + if(ind >= 0){ + this.previewProviders.splice(ind, 1); + this.treeViewWrapper.refreshRootNode(); + } + void vscode.window.showWarningMessage(`Disposing R-Help Previewer for: ${previewer.packageDir}`); + } else{ + for (const panel of this.helpPanels) { + void panel.refreshPreview(previewer.packageDir); + } + this.treeViewWrapper.refreshPreviewNode(previewer.packageDir); + } + } + + // refresh cached help info only for a specific file/package + public clearCachedFiles(re: string | RegExp): void { + for (const path of this.cachedHelpFiles.keys()) { + if ( + (typeof re === 'string' && path === re) || + (typeof re !== 'string' && re.exec(path)) + ) { + this.cachedHelpFiles.delete(path); + } + } + } + + // create a new help panel + public makeNewHelpPanel(panel?: vscode.WebviewPanel): HelpPanel { + const helpPanel = new HelpPanel(this.helpPanelOptions, this, panel); + this.helpPanels.unshift(helpPanel); + return helpPanel; + } + + // return the active help panel + // if no help panel is active and fallBack==true, the newest help panel is returned + // (or a new one created) + public getActiveHelpPanel(): HelpPanel + public getActiveHelpPanel(fallBack?: boolean): HelpPanel | undefined + public getActiveHelpPanel(fallBack: boolean = true): HelpPanel | undefined { + for (const helpPanel of this.helpPanels) { + if (helpPanel.panel && helpPanel.panel.active) { + return helpPanel; + } + } + if (fallBack) { + return this.getNewestHelpPanel(); + } + return undefined; + } + + // return the newest help panel + // if no help panel is available and createNewPanel==true, a new panel is created + public getNewestHelpPanel(): HelpPanel + public getNewestHelpPanel(createNewPanel: boolean): HelpPanel | undefined + public getNewestHelpPanel( + createNewPanel: boolean = true, + ): HelpPanel | undefined { + if (this.helpPanels.length) { + return this.helpPanels[0]; + } else if (createNewPanel) { + return this.makeNewHelpPanel(); + } else { + return undefined; + } + } + + // search function, similar to typing `?? ...` in R + public async searchHelpByText(): Promise { + const searchTerm = await vscode.window.showInputBox({ + value: '', + prompt: 'Please enter a search term', + }); + if (searchTerm !== undefined) { + return this.showHelpForPath( + `/doc/html/Search?pattern=${searchTerm}`, + ); + } + return false; + } + + // quickly open help for selection + public async openHelpForSelection( + preserveFocus: boolean = false, + ): Promise { + // only use if we failed to show help page: + let errMsg = ''; + + const editor = vscode.window.activeTextEditor; + if (editor) { + // the text to show help for: + let txt = ''; + if (editor.selection.isEmpty) { + // no text selected -> find word at current cursor position + // use regex including ":" to capture package/namespace (e.g. base::print) + const re = /([a-zA-Z0-9._:])+/; + const range = editor.document.getWordRangeAtPosition( + editor.selection.start, + re, + ); + // check if the cursor is at a word (else: whitespace -> ignore) + if (range) { + txt = editor.document.getText(range); + } + } else { + // use selected text + txt = editor.document.getText(editor.selection); + } + txt = txt.trim(); + if (txt) { + const success = await this.openHelpByAlias(txt, preserveFocus); + if (!success) { + errMsg = `Failed to open help for "${txt}"!`; + } + } else { + errMsg = 'Cannot show help: No valid text selected!'; + } + } else { + errMsg = 'No editor active!'; + } + if (errMsg) { + void vscode.window.showErrorMessage(errMsg); + return false; + } + return true; + } + + // quickly open help page by alias + public async openHelpByAlias( + token: string = '', + preserveFocus: boolean = false, + ): Promise { + const matchingAliases = await this.getMatchingAliases(token); + + let pickedAlias: Alias | undefined; + if (!matchingAliases?.length) { + return false; + } else if (matchingAliases.length === 1) { + pickedAlias = matchingAliases[0]; + } else { + pickedAlias = await this.pickAlias(matchingAliases); + if (!pickedAlias) { + // aborted by user -> return successful + return true; + } + } + if (pickedAlias) { + return await this.showHelpForAlias(pickedAlias, preserveFocus); + } + return false; + } + + public async getMatchingAliases( + token: string, + ): Promise { + const aliases = await this.getAllAliases(true); + if(!aliases){ + return undefined; + } + + const matchingAliases = aliases.filter( + (alias) => + token === alias.alias || + token === `${alias.package}::${alias.alias}` || + token === `${alias.package}:::${alias.alias}`, + ); + + // Filter out identical aliases. This would cause noticeable delay on the full list. + const aliasesIdentical = (a1: Alias, a2: Alias) => ( + a1.package === a2.package + && a1.name.replace(/^dot-/, '.') === a2.name.replace(/^dot-/, '.') + ); + const uniqueAliases = uniqueEntries(matchingAliases, aliasesIdentical); + + return uniqueAliases; + } + + // search function, similar to calling `?` in R + public async searchHelpByAlias(): Promise { + const alias = await this.pickAlias(); + if (alias) { + return this.showHelpForAlias(alias); + } + return false; + } + + // helper function to get aliases from aliasprovider + private async getAllAliases(includePreview: boolean = false): Promise { + const aliases = await doWithProgress( + () => this.aliasProvider.getAllAliases(), + vscode.ProgressLocation.Window + ); + if (!aliases) { + void vscode.window.showErrorMessage( + `Failed to get list of R functions. Make sure that \`jsonlite\` is installed and r.${getRPathConfigEntry()} points to a valid R executable.`, + ); + return undefined; + } + if(includePreview){ + const previewAliases: Alias[] = this.previewProviders.flatMap(previewer => { + return previewer.getAliases() || []; + }); + aliases.push(...previewAliases); + } + return aliases; + } + + // let the user pick an alias from a supplied list of aliases + // if no list supplied, get all aliases from alias provider + private async pickAlias( + aliases?: Alias[], + prompt?: string, + ): Promise { + prompt ||= 'Please type a function name/documentation entry'; + aliases ||= await this.getAllAliases(); + if (!aliases) { + return undefined; + } + const qpItems: (vscode.QuickPickItem & Alias)[] = aliases.map((v) => { + return { + ...v, + label: v.alias, + description: `(${v.package}::${v.alias})`, + }; + }); + const qpOptions = { + matchOnDescription: true, + placeHolder: prompt, + }; + const qp = await vscode.window.showQuickPick(qpItems, qpOptions); + return qp; + } + + private async showHelpForAlias( + alias: Alias, + preserveFocus: boolean = false, + ): Promise { + return this.showHelpForPath( + `/library/${alias.package}/html/${alias.name}.html`, + undefined, + preserveFocus, + ); + } + + // shows help for request path as used by R's internal help server + public async showHelpForPath( + requestPath: string, + viewer?: string | any, + preserveFocus: boolean = false, + panel?: HelpPanel, + ): Promise { + // get and show helpFile + // const helpFile = this.helpProvider.getHelpFileFromRequestPath(requestPath); + const helpFile = await this.getHelpFileForPath(requestPath); + if (helpFile) { + return this.showHelpFile(helpFile, viewer, preserveFocus, panel); + } else { + const msg = `Couldn't show help for path:\n${requestPath}\n`; + void vscode.window.showErrorMessage(msg); + return false; + } + } + + public async getHelpFileForPath( + requestPath: string, + modify: boolean = true, + skipCache: boolean = false + ): Promise { + // try to get a preview first + const preview = await this.getHelpPreviewForPath(requestPath); + if(preview){ + pimpMyHelp(preview); + return preview; + } + + // get helpFile from helpProvider if not cached + if (skipCache || !this.cachedHelpFiles.has(requestPath)) { + const helpFile = !isGuestSession + ? await this.helpProvider.getHelpFileFromRequestPath(requestPath) + : await rGuestService?.requestHelpContent(requestPath); + this.cachedHelpFiles.set(requestPath, helpFile); + } + + // modify the helpFile (syntax highlighting etc.) + // modifications are optional and cached + const helpFileCached = this.cachedHelpFiles.get(requestPath); + if (!helpFileCached) { + return undefined; + } else if (modify && !helpFileCached.isModified) { + pimpMyHelp(helpFileCached); + } + + // make deep copy to avoid messing with cache + const helpFile = { + ...helpFileCached, + }; + + return helpFile; + } + + private async getHelpPreviewForPath(requestPath: string): Promise { + for (const previewer of this.previewProviders) { + const ret = await previewer.getHelpFileFromRequestPath(requestPath); + if(ret){ + return ret; + } + } + return undefined; + } + + // shows (internal) help file object in webview + private async showHelpFile( + helpFile: HelpFile | Promise, + viewer?: string | any, + preserveFocus: boolean = false, + panel?: HelpPanel, + ): Promise { + panel ||= this.getNewestHelpPanel(); + return await panel.showHelpFile( + helpFile, + undefined, + undefined, + viewer, + preserveFocus, + ); + } +} + +// improves the help display by applying syntax highlighting and adjusting hyperlinks +// only contains modifications that are independent of the webview panel +// (i.e. no modified file paths, scroll position etc.) +function pimpMyHelp(helpFile: HelpFile): HelpFile { + // Retun if the help file is already modified + if (helpFile.isModified) { + return helpFile; + } + + // store original html content + helpFile.html0 = helpFile.html; + + // Make sure the helpfile content is actually html + const re = /^.*', 'ms'); + helpFile.isHtml = (!!re.exec(helpFile.html) || !!re2.exec(helpFile.html)); + if (!helpFile.isHtml) { + const html = escapeHtml(helpFile.html); + helpFile.html = `
${html}
`; + helpFile.isModified = true; + } + + // parse the html string for futher modifications + const $ = cheerio.load(helpFile.html); + + // use .isHtml as proxy for syntax highlighting, clickable
 etc.
+    if(helpFile.isHtml){
+        // Remove style elements specified in the html itself (replaced with custom CSS)
+        $('head style').remove();
+
+        // strip tags: ...
+        const preCodes = $('pre>code');
+        preCodes.each((_, pc) => {
+            $(pc).replaceWith($(pc).html() || '');
+        });
+
+        // Split code examples at empty lines:
+        const codeClickConfig = config().get('helpPanel.clickCodeExamples');
+        const isEnabled = CODE_CLICKS.some(k => codeClickConfig?.[k] !== 'Ignore');
+        if(isEnabled){
+            $('body').addClass('preClickable');
+            const codeSections = $('pre');
+            codeSections.each((i, section) => {
+                const innerHtml = $(section).html();
+                if(!innerHtml){
+                    return;
+                }
+                const newPres = innerHtml.split('\n\n').map(s => s && `
${s}
`); + const newHtml = '
' + newPres.join('\n') + '
'; + $(section).replaceWith(newHtml); + }); + } + if(codeClickConfig?.Click !== 'Ignore'){ + $('body').addClass('preHoverPointer'); + } + + // Apply syntax highlighting: + if (config().get('helpPanel.enableSyntaxHighlighting')) { + // find all code sections, enclosed by
...
+ const codeSections = $('pre'); + + // apply syntax highlighting to each code section: + codeSections.each((i, section) => { + const styledCode = hljs.default.highlight($(section).text() || '', { + language: 'r', + }); + $(section).html(styledCode.value); + }); + } + } + + // Highlight help preview: + if(helpFile.isPreview){ + let rdInfo: string; + if(helpFile.isIndex){ + rdInfo = 'local .Rd files. Might containt non-exported entries that will not be present in the installed Index'; + } else if(helpFile.rdPath && isFileSafe(helpFile.rdPath)){ + const localRdPath = vscode.workspace.asRelativePath(helpFile.rdPath); + const rdUri = vscode.Uri.file(helpFile.rdPath); + const cmdUri = makeWebviewCommandUriString('r.helpPanel.openFileByPath', rdUri.fsPath, true); + rdInfo = `${localRdPath}`; + } else{ + rdInfo = `a local file`; + } + if(helpFile.rPaths?.length){ + const rHrefs = helpFile.rPaths.map(rPath => { + const localRPath = vscode.workspace.asRelativePath(rPath); + if(isFileSafe(rPath)){ + const rUri = vscode.Uri.file(rPath); + const cmdUri = makeWebviewCommandUriString('r.helpPanel.openFileByPath', rUri.fsPath, true); + return `${localRPath}`; + } else{ + return localRPath; + } + }); + rdInfo += `, based on Roxygen comments in ${rHrefs.join(', ')}`; + } + const infoBlock = `
Preview generated from ${rdInfo}.
`; + $('body').prepend(infoBlock); + } + + // replace html of the helpfile + helpFile.html = $.html(); + + // flag help file as modified + helpFile.isModified = true; + + return helpFile; +} diff --git a/src/helpViewer/packages.ts b/src/helpViewer/packages.ts new file mode 100644 index 000000000..77c94c832 --- /dev/null +++ b/src/helpViewer/packages.ts @@ -0,0 +1,486 @@ + +import * as cheerio from 'cheerio'; +import * as vscode from 'vscode'; + +import { RHelp } from '.'; +import { getConfirmation, executeAsTask, doWithProgress, getCranUrl } from '../util'; +import { getPackagesFromCran } from './cran'; + + +// This file implements a rudimentary 'package manager' +// The exported class PackageManager contains methods to +// * list installed packages +// * list help topics from a package +// * let the user pick a package and/or help topic +// * remove installed packages +// * install packages, selected from CRAN using a quickpick + + +// Types of help topics +export enum TopicType { + // "Home page" of a package, e.g. .../base-package.html + HOME, + // An Index file, e.g. list of packages or list of topics in a package + INDEX, + // E.g. DESCRIPTION + META, + // Regular help topic containing help about an R function etc. + NORMAL +} + + +// interface containing information about an individual help topic +export interface Topic { + name: string; + description: string; + type: TopicType; + aliases?: string[]; + helpPath: string; +} +interface TopicExtra extends Topic { + href: string; +} + + +// interface containing info about a package +// can be either installed locally or parsed from the CRAN website +export interface Package { + name: string; + description: string; + + href?: string; + date?: string; + + helpPath?: string; + + isFavorite?: boolean; + isRecent?: boolean; + + isCran?: boolean; + isInstalled?: boolean; +} + +export interface IndexEntry { + name: string; + description: string; + href?: string; +} + +type CachedIndexFiles = {path: string, items: IndexEntry[] | undefined}[]; + + +export interface PackageManagerOptions { + rPath: string, + rHelp: RHelp, + persistentState: vscode.Memento, + cwd?: string +} + + +export class PackageManager { + + readonly rHelp: RHelp; + + readonly state: vscode.Memento; + + readonly cwd?: string; + + // names of packages to be highlighted in the package list + // public favoriteNames: string[] = []; + + public favoriteNames: Set = new Set(); + + + constructor(args: PackageManagerOptions){ + this.rHelp = args.rHelp; + this.state = args.persistentState; + this.cwd = args.cwd; + this.pullFavoriteNames(); + } + + // Functions to force a refresh of listed packages + // Useful e.g. after installing/removing packages + public async refresh(): Promise { + await this.clearCachedFiles(); + this.pullFavoriteNames(); + } + + // Funciton to clear only the cached files regarding an individual package etc. + public async clearCachedFiles(re?: string|RegExp): Promise { + let cache: CachedIndexFiles | undefined; + if(re){ + const oldCache = this.state.get('r.helpPanel.cachedIndexFiles', []); + cache = oldCache.filter(v => !( + (typeof re === 'string' && v.path === re) + || (typeof re !== 'string' && re.exec(v.path)) + )); + } else{ + cache = undefined; + } + await this.state.update('r.helpPanel.cachedIndexFiles', cache); + } + + // Function to add/remove packages from favorites + public addFavorite(pkgName: string): string[] { + this.pullFavoriteNames(); + this.favoriteNames.add(pkgName); + this.pushFavoriteNames(); + return [...this.favoriteNames.values()]; + } + public removeFavorite(pkgName: string): string[] { + this.pullFavoriteNames(); + this.favoriteNames.delete(pkgName); + this.pushFavoriteNames(); + return [...this.favoriteNames.values()]; + } + + // return the index file if cached, else undefined + private getCachedIndexFile(path: string): IndexEntry[] | undefined { + const cache = this.state.get('r.helpPanel.cachedIndexFiles', []); + const ind = cache.findIndex(v => v.path === path); + if(ind < 0){ + return undefined; + } else{ + return cache[ind].items; + } + } + + // Save a new file to the cache (or update existing entry) + private async updateCachedIndexFile(path: string, items: IndexEntry[] | undefined): Promise{ + const cache = this.state.get('r.helpPanel.cachedIndexFiles', []); + const ind = cache.findIndex(v => v.path === path); + if(ind < 0){ + cache.push({ + path: path, + items: items + }); + } else{ + cache[ind].items = items; + } + await this.state.update('r.helpPanel.cachedIndexFiles', cache); + } + + // Private functions used to sync favoriteNames with global state / workspace state + // Is used frequently when list of favorites is shared globally to sync between sessions + private pullFavoriteNames(){ + if(this.state){ + this.favoriteNames = this.state.get('r.helpPanel.favoriteNamesSet') || this.favoriteNames; + } + } + private pushFavoriteNames(){ + if(this.state){ + void this.state.update('r.helpPanel.favoriteNamesSet', this.favoriteNames); + } + } + + // let the user pick and install a package from CRAN + public async pickAndInstallPackages(pickMany: boolean = false): Promise { + const packages = await doWithProgress(() => this.getPackages(true), this.rHelp.treeViewWrapper.viewId); + if(!packages?.length){ + return false; + } + const pkgs = await pickPackages(packages, 'Please select a package.', pickMany); + if(pkgs?.length){ + const pkgsConfirmed = await confirmPackages('Are you sure you want to install these packages?', pkgs); + if(pkgsConfirmed?.length){ + const names = pkgsConfirmed.map(v => v.name); + return await this.installPackages(names, true); + } + } + return false; + } + + // remove a specified package. The packagename is selected e.g. in the help tree-view + public async removePackage(pkgName: string): Promise { + const rPath = this.rHelp.rPath; + const args = ['--silent', '--no-echo', '--no-save', '--no-restore', '-e', `remove.packages('${pkgName}')`]; + const cmd = `${rPath} ${args.join(' ')}`; + const confirmation = 'Yes, remove package!'; + const prompt = `Are you sure you want to remove package ${pkgName}?`; + + if(await getConfirmation(prompt, confirmation, cmd)){ + await executeAsTask('Remove Package', rPath, args, true); + return true; + } else{ + return false; + } + } + + // actually install packages + // confirmation can be skipped (e.g. if the user has confimred before) + public async installPackages(pkgNames: string[], skipConfirmation: boolean = false): Promise { + const rPath = this.rHelp.rPath; + const cranUrl = await getCranUrl('', this.cwd); + const args = [`--silent`, '--no-echo', `-e`, `install.packages(c(${pkgNames.map(v => `'${v}'`).join(',')}),repos='${cranUrl}')`]; + const cmd = `${rPath} ${args.join(' ')}`; + const pluralS = pkgNames.length > 1? 's' : ''; + const confirmation = `Yes, install package${pluralS}!`; + const prompt = `Are you sure you want to install package${pluralS}: ${pkgNames.join(', ')}?`; + + if(skipConfirmation || await getConfirmation(prompt, confirmation, cmd)){ + await executeAsTask('Install Package', rPath, args, true); + return true; + } + return false; + } + + public async updatePackages(skipConfirmation: boolean = false): Promise { + const rPath = this.rHelp.rPath; + const cranUrl = await getCranUrl('', this.cwd); + const args = ['--silent', '--no-echo', '--no-save', '--no-restore', '-e', `update.packages(ask=FALSE,repos='${cranUrl}')`]; + const cmd = `${rPath} ${args.join(' ')}`; + const confirmation = 'Yes, update all packages!'; + const prompt = 'Are you sure you want to update all installed packages? This might take some time!'; + + if(skipConfirmation || await getConfirmation(prompt, confirmation, cmd)){ + await executeAsTask('Update Packages', rPath, args, true); + return true; + } else{ + return false; + } + } + + public async getPackages(fromCran: boolean = false): Promise { + let packages: Package[]|undefined; + this.pullFavoriteNames(); + if(fromCran){ + // Use a placeholder, since multiple different urls are attempted + const CRAN_PATH_PLACEHOLDER = 'CRAN_PATH_PLACEHOLDER'; + + packages = this.getCachedIndexFile(CRAN_PATH_PLACEHOLDER); + if(!packages?.length){ + const cranUrl = await getCranUrl('', this.cwd); + packages = await getPackagesFromCran(cranUrl); + await this.updateCachedIndexFile(CRAN_PATH_PLACEHOLDER, packages); + } + } else{ + packages = await this.getParsedIndexFile(`/doc/html/packages.html`); + if(!packages?.length){ + void vscode.window.showErrorMessage('Help provider not available!'); + } + } + if(packages){ + for(const pkg of packages){ + pkg.isFavorite = this.favoriteNames.has(pkg.name); + pkg.helpPath = ( + pkg.name === 'doc' ? + '/doc/html/packages.html' : + `/library/${pkg.name}/html/00Index.html` + ); + } + } + return packages; + } + + + // parses a package's index file to produce a list of help topics + // highlights ths 'home' topic and adds entries for the package index and DESCRIPTION file + public async getTopics(pkgName: string, summarize: boolean = false): Promise { + + const indexEntries = await this.getParsedIndexFile(`/library/${pkgName}/html/00Index.html`); + + if(!indexEntries){ + return undefined; + } + + const topics: TopicExtra[] = indexEntries.map(v => { + const topic: TopicExtra = { + name: v.name, + description: v.description, + href: v.href || v.name, + type: TopicType.NORMAL, //replaced below + helpPath: '' // replaced below + }; + + topic.type = (topic.name === `${pkgName}-package` ? TopicType.HOME : TopicType.NORMAL); + + topic.helpPath = ( + pkgName === 'doc' ? + `/doc/html/${topic.href}` : + `/library/${pkgName}/html/${topic.href}` + ); + return topic; + }); + + const ind = topics.findIndex(v => v.type === TopicType.HOME); + let homeTopic: TopicExtra | undefined = undefined; + if(ind >= 0){ + homeTopic = topics.splice(ind, 1)[0]; + } + + const indexTopic: TopicExtra = { + name: 'Index', + description: '', + href: '00Index.html', + helpPath: `/library/${pkgName}/html/00Index.html`, + type: TopicType.INDEX + }; + + const descriptionTopic: TopicExtra = { + name: 'DESCRIPTION', + description: '', + href: '../DESCRIPTION', + helpPath: `/library/${pkgName}/DESCRIPTION`, + type: TopicType.META + }; + + topics.unshift(indexTopic, descriptionTopic); + if(homeTopic){ + topics.unshift(homeTopic); + } + + const ret = (summarize ? summarizeTopics(topics) : topics); + + ret.sort((a, b) => { + if(a.type === b.type){ + return a.name.localeCompare(b.name); + } else{ + return a.type - b.type; + } + }); + + return ret; + } + + // retrieve and parse an index file + // (either list of all packages, or documentation entries of a package) + private async getParsedIndexFile(path: string): Promise { + + let indexItems = this.getCachedIndexFile(path); + + // only read and parse file if not cached yet + if(!indexItems){ + const helpFile = await this.rHelp.getHelpFileForPath(path, false); + if(!helpFile?.html){ + // set missing files to null + indexItems = undefined; + } else{ + // parse and cache file + indexItems = parseIndexFile(helpFile.html); + } + void this.updateCachedIndexFile(path, indexItems); + } + + // return cache entry. make new array to avoid messing with the cache + let ret: IndexEntry[] | undefined = undefined; + if(indexItems){ + ret = []; + ret.push(...indexItems); + } + return ret; + } +} + + +function parseIndexFile(html: string): IndexEntry[] { + + const $ = cheerio.load(html); + + const tables = $('table'); + + const ret: IndexEntry[] = []; + + // loop over all tables on document and each row as one index entry + // assumes that the provided html is from a valid index file + tables.each((tableIndex, table) => { + const rows = $('tr', table); + rows.each((rowIndex, row) => { + const elements = $('td', row); + if(elements.length === 2){ + const e0 = elements[0]; + const e1 = elements[1]; + if( + e0.type === 'tag' && e1.type === 'tag' && + e0.firstChild?.type === 'tag' + ){ + const href = e0.firstChild.attribs['href']; + const name = e0.firstChild?.firstChild?.data || ''; + const description = e1.firstChild?.data || ''; + ret.push({ + name: name, + description: description, + href: href, + }); + } + } + }); + }); + + const retSorted = ret.sort((a, b) => a.name.localeCompare(b.name)); + + return retSorted; +} + + +// Used to let the user confirm their choice when installing/removing packages +async function confirmPackages(placeHolder: string, packages: Package[]): Promise { + const qpItems: (vscode.QuickPickItem & {package: Package})[] = packages.map(pkg => ({ + label: pkg.name, + detail: pkg.description, + package: pkg, + picked: true + })); + const qpOptions: vscode.QuickPickOptions = { + matchOnDescription: true, + matchOnDetail: true, + placeHolder: placeHolder + }; + const qp = await vscode.window.showQuickPick(qpItems, {...qpOptions, canPickMany: true}); + const ret = qp?.map(v => v.package) || []; + return ret; +} + +// Let the user pick a package, either from local installation or CRAN +async function pickPackages(packages: Package[], placeHolder: string, pickMany: boolean = false): Promise { + if(!packages?.length){ + return undefined; + } + + const qpItems: (vscode.QuickPickItem & {package: Package})[] = packages.map(pkg => ({ + label: pkg.name, + detail: pkg.description, + package: pkg + })); + + const qpOptions: vscode.QuickPickOptions = { + matchOnDescription: true, + matchOnDetail: true, + placeHolder: placeHolder + }; + let ret: Package | Package[] | undefined; + if(pickMany){ + const qp = await vscode.window.showQuickPick(qpItems, {...qpOptions, canPickMany: true}); + ret = qp?.map(v => v.package); + } else{ + const qp = await vscode.window.showQuickPick(qpItems, qpOptions); + ret = (qp ? [qp.package] : undefined); + } + + return ret; +} + +// Used to summarize index-entries that point to the same help file +function summarizeTopics(topics: Topic[]): Topic[] { + const topicMap = new Map(); + for(const topic of topics){ + if(topicMap.has(topic.helpPath)){ + const newTopic = topicMap.get(topic.helpPath); // checked above that key is present + if(newTopic.aliases){ + newTopic.aliases.push(topic.name); + } + // newTopic.topicType ||= topic.topicType; + newTopic.type = (newTopic.type === TopicType.NORMAL ? topic.type : newTopic.type); + } else{ + const newTopic: Topic = { + ...topic, + }; + if(newTopic.type === TopicType.NORMAL && newTopic.description){ + newTopic.aliases = [newTopic.name]; + [newTopic.name, newTopic.description] = [newTopic.description, newTopic.name]; + } + topicMap.set(newTopic.helpPath, newTopic); + } + } + const newTopics = [...topicMap.values()]; + return newTopics; +} diff --git a/src/helpViewer/panel.ts b/src/helpViewer/panel.ts new file mode 100644 index 000000000..088e4aae1 --- /dev/null +++ b/src/helpViewer/panel.ts @@ -0,0 +1,424 @@ +/* eslint-disable @typescript-eslint/no-unsafe-argument */ +/* eslint-disable @typescript-eslint/no-explicit-any */ + +import * as vscode from 'vscode'; +import * as cheerio from 'cheerio'; + +import { CodeClickConfig, HelpFile, RHelp } from '.'; +import { setContext, UriIcon, config, asViewColumn } from '../util'; +import { runTextInTerm } from '../rTerminal'; +import { OutMessage } from './webviewMessages'; + +//// Declaration of interfaces used/implemented by the Help Panel class +// specified when creating a new help panel +export interface HelpPanelOptions { + /* Local path of script.js, used to send messages to vs code */ + webviewScriptPath: string; + /* Local path of theme.css, used to actually format the highlighted syntax */ + webviewStylePath: string; +} + +// internal interface used to store history of help panel +interface HistoryEntry { + helpFile: HelpFile; + isStale?: boolean; // Used to mark history entries as stale after a refresh +} + +export class HelpPanel { + + private readonly rHelp: RHelp; + + // the webview panel where the help is shown + public panel?: vscode.WebviewPanel; + + // locations on disk, only changed on construction + readonly webviewScriptFile: vscode.Uri; // the javascript added to help pages + readonly webviewStyleFile: vscode.Uri; // the css file applied to help pages + + // virtual locations used by webview, changed each time a new webview is created + private webviewScriptUri?: vscode.Uri; + private webviewStyleUri?: vscode.Uri; + + // keep track of history to go back/forward: + private currentEntry: HistoryEntry | undefined = undefined; + private history: HistoryEntry[] = []; + private forwardHistory: HistoryEntry[] = []; + + // used to get scrollY position from webview: + private scrollYCallback?: (y: number) => void; + + constructor(options: HelpPanelOptions, rHelp: RHelp, panel?: vscode.WebviewPanel) { + this.webviewScriptFile = vscode.Uri.file(options.webviewScriptPath); + this.webviewStyleFile = vscode.Uri.file(options.webviewStylePath); + this.rHelp = rHelp; + if (panel) { + this.panel = panel; + this.initializePanel(); + } + } + + // used to close files, stop servers etc. + public dispose(): void { + if (this.panel) { + this.panel.dispose(); + } + } + + public async refresh(): Promise { + for (const he of [...this.history, ...this.forwardHistory]) { + he.isStale = true; + } + await this.refreshCurrentEntry(); + } + + public async refreshPreview(packageDir: string): Promise { + if(this.currentEntry?.helpFile.packageDir === packageDir){ + await this.refreshCurrentEntry(); + } + } + + private async refreshCurrentEntry(): Promise { + if(!this.currentEntry){ + return; + } + const newHelpFile = await this.rHelp.getHelpFileForPath(this.currentEntry.helpFile.requestPath, undefined, true); + if(!newHelpFile){ + return; + } + newHelpFile.scrollY = await this.getScrollY(); + await this.showHelpFile(newHelpFile, false, undefined, undefined, true); + } + + // retrieves the stored webview or creates a new one if the webview was closed + private getWebview(preserveFocus: boolean = false, viewColumn: vscode.ViewColumn = vscode.ViewColumn.Two): vscode.Webview { + // create webview if necessary + if (!this.panel) { + const webViewOptions: vscode.WebviewOptions & vscode.WebviewPanelOptions = { + enableScripts: true, + enableFindWidget: true, + enableCommandUris: true, + retainContextWhenHidden: true // keep scroll position when not focussed + }; + const showOptions = { + viewColumn: viewColumn, + preserveFocus: preserveFocus + }; + this.panel = vscode.window.createWebviewPanel('rhelp', 'R Help', showOptions, webViewOptions); + this.initializePanel(); + } + + this.panel.reveal(undefined, preserveFocus); + void this.setContextValues(); + + return this.panel.webview; + } + + private initializePanel(): void { + if (!this.panel) { + return; + } + this.panel.iconPath = new UriIcon('help'); + // virtual uris used to access local files + this.webviewScriptUri = this.panel.webview.asWebviewUri(this.webviewScriptFile); + this.webviewStyleUri = this.panel.webview.asWebviewUri(this.webviewStyleFile); + + // called e.g. when the webview panel is closed by the user + this.panel.onDidDispose(() => { + this.panel = undefined; + this.history = []; + this.forwardHistory = []; + this.currentEntry = undefined; + this.webviewScriptUri = undefined; + this.webviewStyleUri = undefined; + void this.setContextValues(); + }); + + // sent by javascript added to the help pages, e.g. when a link or mouse button is clicked + this.panel.webview.onDidReceiveMessage((e: OutMessage) => { + void this.handleMessage(e); + }); + + // set context variable to show forward/backward buttons + this.panel.onDidChangeViewState(() => { + void this.setContextValues(); + }); + } + + + public async setContextValues(): Promise { + await setContext('r.helpPanel.canOpenExternal', !!this.currentEntry?.helpFile.url); + await setContext('r.helpPanel.active', !!this.panel?.active); + await setContext('r.helpPanel.canGoBack', this.history.length > 0); + await setContext('r.helpPanel.canGoForward', this.forwardHistory.length > 0); + } + + // shows (internal) help file object in webview + public async showHelpFile(helpFile: HelpFile | Promise, updateHistory = true, currentScrollY = 0, viewer?: vscode.ViewColumn | string, preserveFocus: boolean = false): Promise { + + viewer ||= config().get('session.viewers.viewColumn.helpPanel'); + const viewColumn = asViewColumn(viewer); + + // get or create webview: + const webview = this.getWebview(preserveFocus, viewColumn); + + // make sure helpFile is not a promise: + helpFile = await helpFile; + + helpFile.scrollY = helpFile.scrollY || 0; + + // modify html + helpFile = await this.pimpMyHelp(helpFile, this.webviewStyleUri, this.webviewScriptUri); + + // actually show the help page + webview.html = helpFile.html; + + // update history to enable back/forward + if (updateHistory) { + if (this.currentEntry) { + this.currentEntry.helpFile.scrollY = currentScrollY; + this.history.push(this.currentEntry); + } + this.forwardHistory = []; + } + this.currentEntry = { + helpFile: helpFile, + isStale: helpFile.isPreview + }; + + await this.setContextValues(); + + return true; + } + + public async openInExternalBrowser(helpFile?: HelpFile): Promise { + if (!this.currentEntry) { + return false; + } + if (!helpFile) { + helpFile = this.currentEntry.helpFile; + } + const url = helpFile.url; + if (!url) { + return false; + } + const uri = vscode.Uri.parse(url); + return vscode.env.openExternal(uri); + } + + // go back/forward in the history of the webview: + public async goBack(): Promise { + const scrollY = await this.getScrollY(); + this._goBack(scrollY); + + } + private _goBack(currentScrollY = 0): void { + const entry = this.history.pop(); + if (entry) { + if (this.currentEntry) { // should always be true + this.currentEntry.helpFile.scrollY = currentScrollY; + this.forwardHistory.push(this.currentEntry); + } + void this.showHistoryEntry(entry); + } + } + public async goForward(): Promise { + const scrollY = await this.getScrollY(); + this._goForward(scrollY); + + } + private _goForward(currentScrollY = 0): void { + const entry = this.forwardHistory.pop(); + if (entry) { + if (this.currentEntry) { // should always be true + this.currentEntry.helpFile.scrollY = currentScrollY; + this.history.push(this.currentEntry); + } + void this.showHistoryEntry(entry); + } + } + private async showHistoryEntry(entry: HistoryEntry) { + let helpFile: HelpFile; + if (entry.isStale) { + // Fallback to stale helpFile. + // Handle differently? + const newHelpFile = await this.rHelp.getHelpFileForPath(entry.helpFile.requestPath, true, true); + helpFile = newHelpFile || entry.helpFile; + helpFile.scrollY = entry.helpFile.scrollY; + } else { + helpFile = entry.helpFile; + } + + void this.showHelpFile(helpFile, false); + } + + // Get current scrollY from webview + private async getScrollY(): Promise { + this.scrollYCallback?.(0); + const scrollYPromise = new Promise((resolve, reject) => { + const timeout = setTimeout(() => reject('GetScrollY message timed out after 1s'), 1000); + this.scrollYCallback = (y: number) => { + clearTimeout(timeout); + this.scrollYCallback = undefined; + resolve(y); + }; + }); + void this.panel?.webview.postMessage({ command: 'getScrollY' }); + return scrollYPromise; + } + + // handle message produced by javascript inside the help page + private async handleMessage(msg: OutMessage) { + if (msg.message === 'linkClicked') { + // handle hyperlinks clicked in the webview + // normal navigation does not work in webviews (even on localhost) + const href: string = msg.href || ''; + const currentScrollY: number = Number(msg.scrollY) || 0; + console.log('Link clicked: ' + href); + + // remove first to path entries (if these are webview internal stuff): + const uri = vscode.Uri.parse(href); + const parts = uri.path.split('/'); + if (parts[0] !== 'library' && parts[0] !== 'doc') { + parts.shift(); + } + if (parts[0] !== 'library' && parts[0] !== 'doc') { + parts.shift(); + } + + // actual request path as used by R: + const requestPath = parts.join('/'); + + // retrieve helpfile for path: + const helpFile = await this.rHelp.getHelpFileForPath(requestPath); + + // if successful, show helpfile: + if (helpFile) { + if (uri.fragment) { + helpFile.hash = '#' + uri.fragment; + } else { + helpFile.scrollY = 0; + } + if (uri.path.endsWith('.pdf')) { + void this.openInExternalBrowser(helpFile); + } else if (uri.path.endsWith('.R')) { + const doc = await vscode.workspace.openTextDocument({ + language: 'r', + content: helpFile.html0 + }); + void vscode.window.showTextDocument(doc); + } else { + void this.showHelpFile(helpFile, true, currentScrollY); + } + } else{ + void vscode.window.showWarningMessage(`Did not find help page for path ${requestPath}`); + } + } else if (msg.message === 'mouseClick') { + // use the additional mouse buttons to go forward/backwards + const currentScrollY = Number(msg.scrollY) || 0; + const button: number = Number(msg.button) || 0; + if (button === 3) { + this._goBack(currentScrollY); + } else if (button === 4) { + this._goForward(currentScrollY); + } + } else if (msg.message === 'codeClicked') { + if (!msg.code) { + return; + } + // Process modifiers: + const isCtrlClick = msg.modifiers.ctrlKey || msg.modifiers.metaKey; + const isShiftClick = msg.modifiers.shiftKey; + const isNormalClick = !isCtrlClick && !isShiftClick; + + // Check wheter to copy or run the code (or both or none) + const codeClickConfig = config().get('helpPanel.clickCodeExamples'); + const runCode = ( + isCtrlClick && codeClickConfig?.['Ctrl+Click'] === 'Run' + || isShiftClick && codeClickConfig?.['Shift+Click'] === 'Run' + || isNormalClick && codeClickConfig?.['Click'] === 'Run' + ); + const copyCode = ( + isCtrlClick && codeClickConfig?.['Ctrl+Click'] === 'Copy' + || isShiftClick && codeClickConfig?.['Shift+Click'] === 'Copy' + || isNormalClick && codeClickConfig?.['Click'] === 'Copy' + ); + + // Execute action: + if (copyCode) { + void vscode.env.clipboard.writeText(msg.code); + void vscode.window.showInformationMessage('Copied code example to clipboard.'); + } + if (runCode) { + void runTextInTerm(msg.code); + } + } else if (msg.message === 'getScrollY') { + this.scrollYCallback?.(msg.scrollY || 0); + } else { + console.log('Unknown message:', msg); + } + } + + // improves the help display by applying syntax highlighting and adjusting hyperlinks: + private async pimpMyHelp(helpFile: HelpFile, styleUri?: vscode.Uri | string, scriptUri?: vscode.Uri | string): Promise { + + // get requestpath of helpfile + const relPath = helpFile.requestPath + (helpFile.hash || ''); + + // parse the html string + const $ = cheerio.load(helpFile.html); + + // set relPath attribute. Used by js inside the page to adjust hyperlinks + // scroll to top (=0) or last viewed position (if the page is from history) + $('body').attr('relpath', relPath); + $('body').attr('scrollyto', `${helpFile.scrollY ?? -1}`); + + if (helpFile.url) { + // replace katex js/css urls with http://localhost:/ origin + // and remove others. + const url = new URL(helpFile.url); + + for (const elem of $('link')) { + const obj = $(elem); + const linkUrl = obj.attr('href'); + if (linkUrl) { + if (linkUrl.includes('katex')) { + const newUrl = new URL(linkUrl, url.origin); + const newUri = await vscode.env.asExternalUri(vscode.Uri.parse(newUrl.toString())); + obj.attr('href', newUri.toString(true)); + } else { + obj.remove(); + } + } + } + + for (const elem of $('script')) { + const obj = $(elem); + const scriptUrl = obj.attr('src'); + if (scriptUrl) { + if (scriptUrl.includes('katex')) { + const newUrl = new URL(scriptUrl, url.origin); + const newUri = await vscode.env.asExternalUri(vscode.Uri.parse(newUrl.toString())); + obj.attr('src', newUri.toString(true)); + } else { + obj.remove(); + } + } + } + } + + if (styleUri) { + $('body').append(`\n`); + } + if (scriptUri) { + $('body').append(`\n`); + } + + + // convert to string + helpFile.html = $.html(); + + // return the html of the modified page: + return helpFile; + } + +} diff --git a/src/helpViewer/treeView.ts b/src/helpViewer/treeView.ts new file mode 100644 index 000000000..00b1f627b --- /dev/null +++ b/src/helpViewer/treeView.ts @@ -0,0 +1,717 @@ +/* eslint-disable @typescript-eslint/no-unsafe-argument */ +import * as vscode from 'vscode'; + +import { RHelp } from '.'; +import { extensionContext } from '../extension'; +import { doWithProgress } from '../util'; +import { RLocalHelpPreviewer } from './helpPreviewer'; +import { Package, Topic, TopicType } from './packages'; + +// this enum is re-assigned just for code readability +const CollapsibleState = vscode.TreeItemCollapsibleState; + + +// the commands contributed in package.json for the tree view +// the commands are registered in HelpTreeWrapper.constructor +// the node-objects only need to handle the keys ('QUICKPICK' etc.) in Node.handleCommand() +const nodeCommands = { + QUICKPICK: 'r.helpPanel.showQuickPick', // called to show the children of a node in a quickpick + CALLBACK: 'r.helpPanel.internalCallback', // called when the item is clicked and node.command is not null + searchPackage: 'r.helpPanel.searchPackage', + openInNewPanel: 'r.helpPanel.openInNewPanel', + clearCache: 'r.helpPanel.clearCache', + removeFromFavorites: 'r.helpPanel.removeFromFavorites', + addToFavorites: 'r.helpPanel.addToFavorites', + removePackage: 'r.helpPanel.removePackage', + updatePackage: 'r.helpPanel.updatePackage', + showOnlyFavorites: 'r.helpPanel.showOnlyFavorites', + showAllPackages: 'r.helpPanel.showAllPackages', + filterPackages: 'r.helpPanel.filterPackages', + summarizeTopics: 'r.helpPanel.summarizeTopics', + unsummarizeTopics: 'r.helpPanel.unsummarizeTopics', + installPackages: 'r.helpPanel.installPackages', + updateInstalledPackages: 'r.helpPanel.updateInstalledPackages' +} as const; + +// used to avoid typos when handling commands +type cmdName = keyof typeof nodeCommands; + + +//////////////////// +// The following classes are mostly just an 'adapter layer' between vscode's treeview interface +// and the object oriented approach used here to present nodes of the treeview. +// The 'interesting' part of the nodes is implemented below + + +// wrapper around vscode.window.createTreeView() +// necessary to implement Node.refresh(), +// which is used to signal from a node that its contents/children have changed +export class HelpTreeWrapper { + public readonly viewId = 'rHelpPages'; + public rHelp: RHelp; + public helpView: vscode.TreeView; + public helpViewProvider: HelpViewProvider; + + constructor(rHelp: RHelp){ + this.rHelp = rHelp; + this.helpViewProvider = new HelpViewProvider(this); + this.helpView = vscode.window.createTreeView( + this.viewId, + { + treeDataProvider: this.helpViewProvider, + showCollapseAll: true + } + ); + + // register the commands defined in `nodeCommands` + // they still need to be defined in package.json (apart from CALLBACK) + for (const cmd in nodeCommands) { + const cmdTyped = cmd as cmdName; // Ok since `cmdName` is defiend as `keyof typeof nodeCommands` + extensionContext.subscriptions.push(vscode.commands.registerCommand(nodeCommands[cmdTyped], (node: Node | undefined) => { + // treeview-root is represented by `undefined`: + node ||= this.helpViewProvider.rootItem; + node.handleCommand(cmdTyped); + })); + } + } + + public refreshNode(node: Node | undefined): void { + for(const listener of this.helpViewProvider.listeners){ + listener(node); + } + } + + public refreshPackageRootNode(): void { + this.helpViewProvider.rootItem?.pkgRootNode?.refresh(); + } + + public refreshPreviewNode(packageDir: string): void { + this.helpViewProvider.rootItem.previewChildren?.forEach(node => { + if(node.packageDir === packageDir){ + node.refresh(true); + } + }); + } + + public refreshRootNode(): void { + this.helpViewProvider.rootItem.refresh(true); + } +} + + +// mostly just a wrapper to implement vscode.TreeDataProvider +export class HelpViewProvider implements vscode.TreeDataProvider { + public rootItem: RootNode; + + public listeners: ((e: Node | undefined) => void)[] = []; + + constructor(wrapper: HelpTreeWrapper){ + this.rootItem = new RootNode(wrapper); + } + + onDidChangeTreeData(listener: (e: Node | undefined) => void): vscode.Disposable { + this.listeners.push(listener); + return new vscode.Disposable(() => { + // do nothing + }); + } + + getChildren(element?: Node): vscode.ProviderResult{ + element ||= this.rootItem; + return element.getChildren(); + } + getTreeItem(element: Node): Node { + return element; + } + getParent(element: Node): Node | undefined { + return element.parent; + } +} + +// Abstract base class for nodes of the treeview +// Is a rather technical base class to handle the intricacies of vscode's treeview API +// All the 'interesting' stuff hapens in the derived classes +// New commands should (if possible) be implemented by defining a new derived class, +// rather than modifying this class! +abstract class Node extends vscode.TreeItem{ + // TreeItem (defaults for this usecase) + declare public description?: string; + public collapsibleState: vscode.TreeItemCollapsibleState = vscode.TreeItemCollapsibleState.None; + public contextValue: string = ''; + declare public label?: string; + declare public tooltip?: string; + + // set to null/undefined in derived class to expand/collapse on click + public command = { + title: 'treeNodeCallback', // is this title used anywhere? + command: nodeCommands.CALLBACK, + arguments: [this] + } as vscode.Command | undefined; + + // Node + public parent: Node | undefined = undefined; + public children?: Node[] = undefined; + + // These can be used to modify the behaviour of a node when showed as/in a quickpick: + public quickPickCommand?: cmdName; // defaults to this.showQuickPick or callBack + public qpLabel?: string; // defaults to node.icon + node.label + public qpDetail?: string; // defaults to node.detail || node.description || node.toolTip + public qpPrompt?: string; // defaults to empty input bar + + // These are shared between nodes to access functions of the help panel etc. + // could also be static? + readonly wrapper: HelpTreeWrapper; + readonly rootNode?: RootNode; + + // used to give unique ids to nodes + static newId: number = 0; + + // The default constructor just copies some info from parent + constructor(parent: Node | undefined, wrapper: HelpTreeWrapper){ + super(''); + this.wrapper = wrapper; + if(parent){ + this.parent = parent; + this.rootNode = parent.rootNode; + } + this.id = `${Node.newId++}`; + } + + // Called when a node or command-button on a node is clicked + // Only internal commands are handled here, custom commands are implemented in _handleCommand! + public handleCommand(cmd: cmdName){ + if(cmd === 'CALLBACK' && this.callBack){ + void this.callBack(); + } else if(cmd === 'QUICKPICK'){ + if(this.quickPickCommand){ + this._handleCommand(this.quickPickCommand); + } else if(this.collapsibleState !== CollapsibleState.None){ + void this.showQuickPick(); + } else{ + this.handleCommand('CALLBACK'); + } + } else { + this._handleCommand(cmd); + } + } + + // overwrite this in derived classes to handle custom commands + protected _handleCommand(cmd: cmdName): void; + protected _handleCommand(){ + // to be overwritten + } + + // implement this to handle callBacks (simple clicks on a node) + // can also be implemented in _handleCommand('CALLBACK') + public callBack?(): void | Promise; + + // Shows a quickpick containing the children of a node + // If the picked child has children itself, another quickpick is shown + // Otherwise, its QUICKPICK or CALLBACK command is executed + public async showQuickPick(){ + const children = await this.makeChildren(true); + if(!children){ + return undefined; + } + const qpItems: (vscode.QuickPickItem & {child: Node})[] = children.map(v => { + let label = v.label || ''; + if(typeof v.iconPath === 'object' && 'id' in v.iconPath){ + label = `$(${v.iconPath.id}) ${label}`; + } + return { + label: v.qpLabel ?? label, + detail: v.qpDetail ?? v.description ?? v.tooltip, + child: v + }; + }); + const qp = await vscode.window.showQuickPick(qpItems, { + placeHolder: this.qpPrompt + }); + if(qp){ + const child = qp.child; + child.handleCommand('QUICKPICK'); + } + } + + // Called by vscode etc. to get the children of a node + // Not meant to be modified in derived classes! + public async getChildren(): Promise { + if(this.children === undefined){ + this.children = await this.makeChildren(); + } + return this.children; + } + + // to be overwritten, if the node has any children + protected makeChildren(forQuickPick?: boolean): Promise | Node[] | undefined; + protected makeChildren(): Promise | Node[] | undefined { + return []; + } + + // Can be called by a method from the node itself or externally to refresh the node in the treeview + public refresh(refreshChildren: boolean = true){ + if(refreshChildren){ + this.children = undefined; + } + this.wrapper.refreshNode(this); + } + + // Clear 'grandchildren' without triggering the treeview to update too often + public refreshChildren(){ + if(this.children){ + for(const child of this.children){ + child.children = undefined; + } + } + } + + // show/focus the node in the treeview + public reveal(options?: { select?: boolean, focus?: boolean, expand?: boolean | number }){ + void this.wrapper.helpView.reveal(this, options); + } + + // These methods are used to update this.contextValue with possible command names + // The constructed contextValue contains the command names of the commands applying to this node + static makeContextValue(...args: cmdName[]){ + return args.map(v => `_${v}_`).join(''); + } + public addContextValues(...args: cmdName[]){ + args.forEach(val => { + this.contextValue += `_${val}_`; + }); + return this.contextValue; + } + public removeContextValues(...args: cmdName[]){ + args.forEach(val => { + this.contextValue = this.contextValue.replace(new RegExp(`_${val}_`), ''); + }); + return this.contextValue; + } + public replaceContextValue(oldCmd: cmdName, newCmd: cmdName){ + this.removeContextValues(oldCmd); + return this.addContextValues(newCmd); + } +} + +abstract class NonRootNode extends Node { + parent: RootNode | NonRootNode; + rootNode: RootNode; + public constructor(parent: RootNode | NonRootNode){ + super(parent, parent.wrapper); + this.parent = parent; + this.rootNode = parent.rootNode; + } +} + + + + +/////////////////////////////////// +// The following classes contain the implementation of the help-view-specific behaviour +// PkgRootNode, PackageNode, and TopicNode are a bit more complex +// The remaining nodes mostly just contain an icon and a callback + + + +// Root of the node. Is not actually used by vscode, but as 'imaginary' root item. +class RootNode extends Node { + public collapsibleState = vscode.TreeItemCollapsibleState.Expanded; + public label = 'root'; + readonly rootNode = this; + + public pkgRootNode?: PkgRootNode; + public staticChildren?: NonRootNode[]; + public previewChildren?: PreviewPackageNode[]; + + constructor(wrapper: HelpTreeWrapper){ + super(undefined, wrapper); + } + makeChildren(){ + this.pkgRootNode ||= new PkgRootNode(this); + this.staticChildren ||= [ + new HomeNode(this), + new Search1Node(this), + new Search2Node(this), + new OpenForSelectionNode(this), + new RefreshNode(this), + new InstallPackageNode(this), + this.pkgRootNode, + ]; + this.previewChildren ||= this.wrapper.rHelp.previewProviders.map( + previewer => new PreviewPackageNode(this, previewer) + ); + return [...this.staticChildren, ...this.previewChildren]; + } + public refresh(refreshChildren: boolean = true){ + if(refreshChildren){ + this.children = undefined; + this.previewChildren = undefined; + } + this.wrapper.refreshNode(undefined); + } +} + +// contains the list of installed packages +class PkgRootNode extends NonRootNode { + // TreeItem + public label = 'Help Topics by Package'; + public iconPath = new vscode.ThemeIcon('list-unordered'); + public description = ''; + public command = undefined; + public collapsibleState = CollapsibleState.Collapsed; + public contextValue = Node.makeContextValue('QUICKPICK', 'clearCache', 'filterPackages', 'showOnlyFavorites', 'unsummarizeTopics'); + + // Node + declare public children?: PackageNode[]; + + // quickpick + public qpPrompt = 'Please select a package.'; + + // PkgRootNode + public showOnlyFavorites: boolean = false; + public filterText?: string; + public summarizeTopics: boolean = true; + + async _handleCommand(cmd: cmdName){ + if(cmd === 'clearCache'){ + // used e.g. after manually installing/removing a package + this.refresh(true); + } else if(cmd === 'showOnlyFavorites'){ + this.showOnlyFavorites = true; + this.iconPath = new vscode.ThemeIcon('star-full'); + this.replaceContextValue('showOnlyFavorites', 'showAllPackages'); + this.refresh(); + } else if(cmd === 'showAllPackages'){ + this.showOnlyFavorites = false; + this.iconPath = new vscode.ThemeIcon('list-unordered'); + this.replaceContextValue('showAllPackages', 'showOnlyFavorites'); + this.refresh(); + } else if(cmd === 'filterPackages'){ + // use validation function to continuously update filtered packages + const validateInput = (value: string) => { + this.filterText = value; + this.refresh(); + return ''; + }; + // let user input filter text + this.filterText = await vscode.window.showInputBox({ + validateInput: validateInput, + value: this.filterText, + }); + this.description = (this.filterText ? `"${this.filterText}"` : ''); + this.refresh(); + } else if(cmd === 'unsummarizeTopics'){ + this.summarizeTopics = false; + this.replaceContextValue('unsummarizeTopics', 'summarizeTopics'); + this.refreshChildren(); // clears the 'grandchildren' + this.refresh(false, false); + } else if(cmd === 'summarizeTopics'){ + this.summarizeTopics = true; + this.replaceContextValue('summarizeTopics', 'unsummarizeTopics'); + this.refreshChildren(); // clears the 'grandchildren' + this.refresh(false, false); + } + } + + refresh(clearCache: boolean = false, refreshChildren: boolean = true){ + if(clearCache){ + this.wrapper.rHelp.clearCachedFiles(`/doc/html/packages.html`); + void this.wrapper.rHelp.packageManager.clearCachedFiles(`/doc/html/packages.html`); + } + super.refresh(refreshChildren); + } + + async makeChildren() { + let packages = await this.wrapper.rHelp.packageManager.getPackages(false); + + if(!packages){ + return []; + } + + if(this.filterText){ + const re = new RegExp(this.filterText); + packages = packages.filter(pkg => re.exec(pkg.name)); + } + + // favorites at the top + const children = packages.filter(pkg => pkg.isFavorite); + + // nonFavorites below (if shown) + if(!this.showOnlyFavorites){ + children.push(...packages.filter(pkg => !pkg.isFavorite)); + } + + // make packageNode for each child + return children.map( + pkg => new PackageNode(this, pkg) + ); + } +} + + +// contains the topics belonging to an individual package +export class PackageNode extends NonRootNode { + // TreeItem + public command = undefined; + public collapsibleState = CollapsibleState.Collapsed; + public contextValue = Node.makeContextValue('QUICKPICK', 'clearCache', 'removePackage', 'updatePackage'); + + // QuickPick + public qpPrompt = 'Please select a Topic.'; + + // Package + public pkg: Package; + + constructor(parent: PkgRootNode, pkg: Package){ + super(parent); + this.pkg = pkg; + this.label = pkg.name; + this.tooltip = pkg.description; + this.qpDetail = pkg.description; + if(this.pkg.isFavorite){ + this.addContextValues('removeFromFavorites'); + } else{ + this.addContextValues('addToFavorites'); + } + if(this.pkg.isFavorite && !this.rootNode.pkgRootNode?.showOnlyFavorites){ + this.iconPath = new vscode.ThemeIcon('star-full'); + } + } + + public async _handleCommand(cmd: cmdName): Promise { + if(cmd === 'clearCache'){ + // useful e.g. when working on a package + this.wrapper.rHelp.clearCachedFiles(new RegExp(`^/library/${this.pkg.name}/`)); + this.refresh(); + } else if(cmd === 'addToFavorites'){ + this.wrapper.rHelp.packageManager.addFavorite(this.pkg.name); + this.parent.refresh(); + } else if(cmd === 'removeFromFavorites'){ + this.wrapper.rHelp.packageManager.removeFavorite(this.pkg.name); + this.parent.refresh(); + } else if(cmd === 'updatePackage'){ + const success = await this.wrapper.rHelp.packageManager.installPackages([this.pkg.name]); + // only reinstall if user confirmed removing the package (success === true) + // might still refresh if install was attempted but failed + if(success){ + this.parent.refresh(true); + } + } else if(cmd === 'removePackage'){ + const success = await this.wrapper.rHelp.packageManager.removePackage(this.pkg.name); + // only refresh if user confirmed removing the package (success === true) + // might still refresh if removing was attempted but failed + if(success){ + this.parent.refresh(true); + } + } + } + + async makeChildren(forQuickPick: boolean = false): Promise { + const summarizeTopics = ( + forQuickPick ? false : (this.rootNode.pkgRootNode?.summarizeTopics ?? true) + ); + const topics = await this.wrapper.rHelp.packageManager.getTopics(this.pkg.name, summarizeTopics); + const ret = topics?.map(topic => new TopicNode(this, topic)) || []; + return ret; + } +} + +// Node representing an individual topic/help page +class TopicNode extends NonRootNode { + // TreeItem + iconPath = new vscode.ThemeIcon('circle-filled'); + contextValue = Node.makeContextValue('openInNewPanel'); + + // Topic + topic: Topic; + + static iconPaths = new Map([ + [TopicType.HOME, 'home'], + [TopicType.INDEX, 'list-unordered'], + [TopicType.META, 'file-code'], + [TopicType.NORMAL, 'circle-filled'] + ]); + + protected _handleCommand(cmd: cmdName){ + if(cmd === 'CALLBACK'){ + void this.wrapper.rHelp.showHelpForPath(this.topic.helpPath); + } else if(cmd === 'openInNewPanel'){ + void this.wrapper.rHelp.makeNewHelpPanel(); + void this.wrapper.rHelp.showHelpForPath(this.topic.helpPath); + } + } + + constructor(parent: NonRootNode, topic: Topic){ + super(parent); + this.topic = topic; + this.label = topic.name; + this.iconPath = new vscode.ThemeIcon(TopicNode.iconPaths.get(this.topic.type) || 'circle-filled'); + if(this.topic.type === TopicType.NORMAL){ + this.qpLabel = this.topic.name; + } + if(this.topic.aliases){ + this.tooltip = `Aliases:\n - ${this.topic.aliases.join('\n - ')}`; + } else{ + this.tooltip = this.topic.description; + } + } +} + +///////////// +// Preview for documentation of local package +class PreviewPackageNode extends NonRootNode { + public label = 'Local Preview'; + public collapsibleState = CollapsibleState.Collapsed; + public iconPath = new vscode.ThemeIcon('eye'); + public command = undefined; + public contextValue = Node.makeContextValue('QUICKPICK', 'unsummarizeTopics'); + public qpPrompt = 'Please select a Topic.' + + public summarizeTopics: boolean = true; + + public packageDir: string; + + private helpPreview: RLocalHelpPreviewer; + + constructor(parent: RootNode, helpPreview: RLocalHelpPreviewer){ + super(parent); + this.helpPreview = helpPreview; + this.packageDir = helpPreview?.packageDir; + this.refreshMetaInfo(); + } + + _handleCommand(cmd: cmdName){ + if(cmd === 'unsummarizeTopics'){ + this.summarizeTopics = false; + this.replaceContextValue('unsummarizeTopics', 'summarizeTopics'); + this.refreshChildren(); // clears the 'grandchildren' + this.refresh(true); + } else if(cmd === 'summarizeTopics'){ + this.summarizeTopics = true; + this.replaceContextValue('summarizeTopics', 'unsummarizeTopics'); + this.refreshChildren(); // clears the 'grandchildren' + this.refresh(true); + } + } + + makeChildren(forQuickPick: boolean = false): TopicNode[] { + const summarizeTopics = ( + forQuickPick ? false : (this.summarizeTopics ?? true) + ); + const topics = this.helpPreview?.getTreeViewTopics(summarizeTopics) || []; + const ret = topics.map(topic => new TopicNode(this, topic)) || []; + return ret; + } + + private refreshMetaInfo(): void { + this.label = `Preview: ${this.helpPreview.getPackageName()}`; + const pkgInfo = this.helpPreview.getPackageInfo(); + const toolTipParts: string[] = []; + if(pkgInfo?.version){ + toolTipParts.push('v' + pkgInfo.version); + } + if(pkgInfo?.title){ + toolTipParts.push(pkgInfo.title); + } + this.tooltip = toolTipParts.join(' - '); + } + + // Can be called by a method from the node itself or externally to refresh the node in the treeview + public refresh(refreshChildren: boolean = true){ + this.refreshMetaInfo(); + if(refreshChildren){ + this.children = undefined; + } + this.wrapper.refreshNode(this); + } +} + + + +///////////// +// The following nodes only implement an individual command each + +class HomeNode extends NonRootNode { + label = 'Home'; + collapsibleState = CollapsibleState.None; + iconPath = new vscode.ThemeIcon('home'); + contextValue = Node.makeContextValue('openInNewPanel'); + + _handleCommand(cmd: cmdName){ + if(cmd === 'openInNewPanel'){ + void this.wrapper.rHelp.makeNewHelpPanel(); + void this.wrapper.rHelp.showHelpForPath('doc/html/index.html'); + } + } + + callBack(){ + void this.wrapper.rHelp.showHelpForPath('doc/html/index.html'); + } +} + +class Search1Node extends NonRootNode { + label = 'Open Help Topic using `?`'; + iconPath = new vscode.ThemeIcon('zap'); + + callBack(){ + void this.wrapper.rHelp.searchHelpByAlias(); + } +} + +class Search2Node extends NonRootNode { + label = 'Search Help Topics using `??`'; + iconPath = new vscode.ThemeIcon('search'); + + callBack(){ + void this.wrapper.rHelp.searchHelpByText(); + } +} + +class RefreshNode extends NonRootNode { + label = 'Clear Cache & Restart Help Server'; + iconPath = new vscode.ThemeIcon('refresh'); + + async callBack(){ + await doWithProgress(() => this.wrapper.rHelp.refresh(), this.wrapper.viewId); + this.rootNode.pkgRootNode?.refresh(); + this.rootNode.refresh(); + } +} + +class OpenForSelectionNode extends NonRootNode { + label = 'Open Help Page for Selected Text'; + iconPath = new vscode.ThemeIcon('symbol-key'); + + callBack(){ + void this.wrapper.rHelp.openHelpForSelection(); + } +} + +class InstallPackageNode extends NonRootNode { + label = 'Install CRAN Package'; + iconPath = new vscode.ThemeIcon('cloud-download'); + + contextValue = Node.makeContextValue('installPackages', 'updateInstalledPackages'); + + public async _handleCommand(cmd: cmdName){ + if(cmd === 'installPackages'){ + const ret = await this.wrapper.rHelp.packageManager.pickAndInstallPackages(true); + if(ret){ + this.rootNode.pkgRootNode?.refresh(true); + } + } else if(cmd === 'updateInstalledPackages'){ + const ret = await this.wrapper.rHelp.packageManager.updatePackages(); + if(ret){ + this.rootNode.pkgRootNode?.refresh(true); + } + } + } + + async callBack(){ + await this.wrapper.rHelp.packageManager.pickAndInstallPackages(); + this.rootNode.pkgRootNode?.refresh(true); + } +} + + + diff --git a/src/helpViewer/webviewMessages.d.ts b/src/helpViewer/webviewMessages.d.ts new file mode 100644 index 000000000..13a230fe9 --- /dev/null +++ b/src/helpViewer/webviewMessages.d.ts @@ -0,0 +1,49 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ + +// Make sure these files contain the same interfaces as ./html/XXX/webviewMessages.d.ts! + + +interface VsCode { + postMessage: (msg: OutMessage) => void; + setState: (state: string) => void; +} +declare function acquireVsCodeApi(): VsCode; + + + +export interface IMessage { + message: string; +} + +export interface LogMessage extends IMessage { + message: 'log', + body: any +} +export interface MouseClickMessage extends IMessage { + message: 'mouseClick', + button: number, + scrollY: number +} +export interface LinkClickedMessage extends IMessage { + message: 'linkClicked', + href: string, + scrollY: number +} +export interface CodeClickedMessage extends IMessage { + message: 'codeClicked', + code: string, + modifiers: { + altKey: boolean, + ctrlKey: boolean, + shiftKey: boolean, + metaKey: boolean, + } +} +export interface GetScrollYMessage extends IMessage { + message: 'getScrollY', + scrollY: number +} + +export type OutMessage = LogMessage | MouseClickMessage | LinkClickedMessage | CodeClickedMessage | GetScrollYMessage; + + diff --git a/src/languageService.ts b/src/languageService.ts new file mode 100644 index 000000000..73a9bec62 --- /dev/null +++ b/src/languageService.ts @@ -0,0 +1,341 @@ +import * as os from 'os'; +import { dirname } from 'path'; +import * as net from 'net'; +import { URL } from 'url'; +import { LanguageClient, LanguageClientOptions, StreamInfo, DocumentFilter, ErrorAction, CloseAction, RevealOutputChannelOn } from 'vscode-languageclient/node'; +import { Disposable, workspace, Uri, TextDocument, WorkspaceConfiguration, OutputChannel, window, WorkspaceFolder } from 'vscode'; +import { DisposableProcess, getRLibPaths, getRpath, promptToInstallRPackage, spawn, substituteVariables } from './util'; +import { extensionContext } from './extension'; +import { CommonOptions } from 'child_process'; + +export class LanguageService implements Disposable { + private readonly clients: Map = new Map(); + private readonly initSet: Set = new Set(); + private readonly config: WorkspaceConfiguration; + private readonly outputChannel: OutputChannel; + + constructor() { + this.outputChannel = window.createOutputChannel('R Language Server'); + this.config = workspace.getConfiguration('r'); + void this.startLanguageService(); + } + + dispose(): Thenable { + return this.stopLanguageService(); + } + + private spawnServer(client: LanguageClient, rPath: string, args: readonly string[], options: CommonOptions & { cwd: string }): DisposableProcess { + const childProcess = spawn(rPath, args, options); + const pid = childProcess.pid || -1; + client.outputChannel.appendLine(`R Language Server (${pid}) started`); + childProcess.stderr.on('data', (chunk: Buffer) => { + client.outputChannel.appendLine(chunk.toString()); + }); + childProcess.on('exit', (code, signal) => { + client.outputChannel.appendLine(`R Language Server (${pid}) exited ` + + (signal ? `from signal ${signal}` : `with exit code ${code || 'null'}`)); + if (code !== 0) { + if (code === 10) { + // languageserver is not installed. + void promptToInstallRPackage( + 'languageserver', 'lsp.promptToInstall', options.cwd, + 'R package {languageserver} is required to enable R language service features such as code completion, function signature, find references, etc. Do you want to install it?', + 'You may need to reopen an R file to start the language service after the package is installed.' + ); + } else { + client.outputChannel.show(); + } + } + void client.stop(); + }); + return childProcess; + } + + private async createClient(selector: DocumentFilter[], + cwd: string, workspaceFolder: WorkspaceFolder | undefined, outputChannel: OutputChannel): Promise { + + let client: LanguageClient; + + const debug = this.config.get('lsp.debug'); + const useRenvLibPath = this.config.get('useRenvLibPath') ?? false; + const rPath = await getRpath() || ''; // TODO: Abort gracefully + if (debug) { + console.log(`R path: ${rPath}`); + } + const use_stdio = this.config.get('lsp.use_stdio'); + const env = Object.create(process.env) as NodeJS.ProcessEnv; + env.VSCR_LSP_DEBUG = debug ? 'TRUE' : 'FALSE'; + env.VSCR_LIB_PATHS = getRLibPaths(); + env.VSCR_USE_RENV_LIB_PATH = useRenvLibPath ? 'TRUE' : 'FALSE'; + + const lang = this.config.get('lsp.lang'); + if (lang !== '') { + env.LANG = lang; + } else if (env.LANG === undefined) { + env.LANG = 'en_US.UTF-8'; + } + + if (debug) { + // eslint-disable-next-line @typescript-eslint/restrict-template-expressions + console.log(`LANG: ${env.LANG}`); + } + + const rScriptPath = extensionContext.asAbsolutePath('R/languageServer.R'); + const options = { cwd: cwd, env: env }; + const args = (this.config.get('lsp.args')?.map(substituteVariables) ?? []).concat( + '--silent', + '--no-echo', + '--no-save', + '--no-restore', + '-e', + 'base::source(base::commandArgs(TRUE))', + '--args', + rScriptPath + ); + + const tcpServerOptions = () => new Promise((resolve, reject) => { + // Use a TCP socket because of problems with blocking STDIO + const server = net.createServer(socket => { + // 'connection' listener + console.log('R process connected'); + socket.on('end', () => { + console.log('R process disconnected'); + }); + socket.on('error', (e: Error) => { + console.log(`R process error: ${e.message}`); + reject(e); + }); + server.close(); + resolve({ reader: socket, writer: socket }); + }); + // Listen on random port + server.listen(0, '127.0.0.1', () => { + const port = (server.address() as net.AddressInfo).port; + env.VSCR_LSP_PORT = String(port); + return this.spawnServer(client, rPath, args, options); + }); + }); + + // Options to control the language client + const clientOptions: LanguageClientOptions = { + // Register the server for selected R documents + documentSelector: selector, + uriConverters: { + // VS Code by default %-encodes even the colon after the drive letter + // NodeJS handles it much better + code2Protocol: uri => new URL(uri.toString(true)).toString(), + protocol2Code: str => Uri.parse(str) + }, + workspaceFolder: workspaceFolder, + outputChannel: outputChannel, + synchronize: { + // Synchronize the setting section 'r' to the server + configurationSection: 'r.lsp', + fileEvents: workspace.createFileSystemWatcher('**/*.{R,r}'), + }, + revealOutputChannelOn: RevealOutputChannelOn.Never, + errorHandler: { + error: () => { + return { + action: ErrorAction.Continue + }; + }, + closed: () => { + return { + action: CloseAction.DoNotRestart + }; + }, + }, + }; + + // Create the language client and start the client. + if (use_stdio && process.platform !== 'win32') { + client = new LanguageClient('r', 'R Language Server', { command: rPath, args: args, options: options }, clientOptions); + } else { + client = new LanguageClient('r', 'R Language Server', tcpServerOptions, clientOptions); + } + + extensionContext.subscriptions.push(client); + await client.start(); + return client; + } + + + private checkClient(name: string): boolean { + if (this.initSet.has(name)) { + return true; + } + const client = this.clients.get(name); + if (client && client.needsStop()) { + return true; + } + this.initSet.add(name); + return false; + } + + private getKey(uri: Uri): string { + switch (uri.scheme) { + case 'untitled': + return uri.scheme; + case 'vscode-notebook-cell': + return `vscode-notebook:${uri.fsPath}`; + default: + return uri.toString(true); + } + } + + private startMultiLanguageService(): void { + const didOpenTextDocument = async (document: TextDocument) => { + if (document.uri.scheme !== 'file' && document.uri.scheme !== 'untitled' && document.uri.scheme !== 'vscode-notebook-cell') { + return; + } + + if (document.languageId !== 'r' && document.languageId !== 'rmd') { + return; + } + + const folder = workspace.getWorkspaceFolder(document.uri); + + // Each notebook uses a server started from parent folder + if (document.uri.scheme === 'vscode-notebook-cell') { + const key = this.getKey(document.uri); + if (!this.checkClient(key)) { + console.log(`Start language server for ${document.uri.toString(true)}`); + const documentSelector: DocumentFilter[] = [ + { scheme: 'vscode-notebook-cell', language: 'r', pattern: `${document.uri.fsPath}` }, + ]; + const client = await this.createClient(documentSelector, + dirname(document.uri.fsPath), folder, this.outputChannel); + this.clients.set(key, client); + this.initSet.delete(key); + } + return; + } + + if (folder) { + + // Each workspace uses a server started from the workspace folder + const key = this.getKey(folder.uri); + if (!this.checkClient(key)) { + console.log(`Start language server for ${document.uri.toString(true)}`); + const pattern = `${folder.uri.fsPath}/**/*`; + const documentSelector: DocumentFilter[] = [ + { scheme: 'file', language: 'r', pattern: pattern }, + { scheme: 'file', language: 'rmd', pattern: pattern }, + ]; + const client = await this.createClient(documentSelector, folder.uri.fsPath, folder, this.outputChannel); + this.clients.set(key, client); + this.initSet.delete(key); + } + + } else { + + // All untitled documents share a server started from home folder + if (document.uri.scheme === 'untitled') { + const key = this.getKey(document.uri); + if (!this.checkClient(key)) { + console.log(`Start language server for ${document.uri.toString(true)}`); + const documentSelector: DocumentFilter[] = [ + { scheme: 'untitled', language: 'r' }, + { scheme: 'untitled', language: 'rmd' }, + ]; + const client = await this.createClient(documentSelector, os.homedir(), undefined, this.outputChannel); + this.clients.set(key, client); + this.initSet.delete(key); + } + return; + } + + // Each file outside workspace uses a server started from parent folder + if (document.uri.scheme === 'file') { + const key = this.getKey(document.uri); + if (!this.checkClient(key)) { + console.log(`Start language server for ${document.uri.toString(true)}`); + const documentSelector: DocumentFilter[] = [ + { scheme: 'file', pattern: document.uri.fsPath }, + ]; + const client = await this.createClient(documentSelector, + dirname(document.uri.fsPath), undefined, this.outputChannel); + this.clients.set(key, client); + this.initSet.delete(key); + } + return; + } + } + }; + + const didCloseTextDocument = (document: TextDocument): void => { + if (document.uri.scheme === 'untitled') { + const result = workspace.textDocuments.find((doc) => doc.uri.scheme === 'untitled'); + if (result) { + // Stop the language server when all untitled documents are closed. + return; + } + } + + if (document.uri.scheme === 'vscode-notebook-cell') { + const result = workspace.textDocuments.find((doc) => + doc.uri.scheme === document.uri.scheme && doc.uri.fsPath === document.uri.fsPath); + if (result) { + // Stop the language server when all cell documents are closed (notebook closed). + return; + } + } + + // Stop the language server when single file outside workspace is closed, or the above cases. + const key = this.getKey(document.uri); + const client = this.clients.get(key); + if (client) { + this.clients.delete(key); + this.initSet.delete(key); + void client.stop(); + } + }; + + workspace.onDidOpenTextDocument(didOpenTextDocument); + workspace.onDidCloseTextDocument(didCloseTextDocument); + workspace.textDocuments.forEach((doc) => void didOpenTextDocument(doc)); + workspace.onDidChangeWorkspaceFolders((event) => { + for (const folder of event.removed) { + const key = this.getKey(folder.uri); + const client = this.clients.get(key); + if (client) { + this.clients.delete(key); + this.initSet.delete(key); + void client.stop(); + } + } + }); + } + + private async startLanguageService(): Promise { + let useMultiServer = false; + const multiServerConfig = this.config.get('lsp.multiServer'); + + if (multiServerConfig === true) { + useMultiServer = true; + } + + if (useMultiServer) { + this.startMultiLanguageService(); + } else { + const documentSelector: DocumentFilter[] = [ + { language: 'r' }, + { language: 'rmd' }, + ]; + + const workspaceFolder = workspace.workspaceFolders?.[0]; + const cwd = workspaceFolder ? workspaceFolder.uri.fsPath : os.homedir(); + const client = await this.createClient(documentSelector, cwd, undefined, this.outputChannel); + this.clients.set('global', client); + } + } + + private stopLanguageService(): Thenable { + const promises: Thenable[] = []; + for (const client of this.clients.values()) { + promises.push(client.stop()); + } + return Promise.all(promises).then(() => undefined); + } +} diff --git a/src/lineCache.ts b/src/lineCache.ts index 0ded21567..3efb9abd9 100644 --- a/src/lineCache.ts +++ b/src/lineCache.ts @@ -1,50 +1,75 @@ -"use strict"; +'use strict'; /** * Class to hold lines that have been fetched from the document after they have been preprocessed. */ export class LineCache { - public lineCache: Map; public endsInOperatorCache: Map; public getLine: (line: number) => string; + public lineCache: Map; public lineCount: number; - constructor(getLine: (line: number) => string, lineCount: number) { + public constructor(getLine: (line: number) => string, lineCount: number) { this.getLine = getLine; this.lineCount = lineCount; this.lineCache = new Map(); this.endsInOperatorCache = new Map(); } - public getLineFromCache(line: number) { - const lineInCache = this.lineCache.has(line); - if (!lineInCache) { - this.addLineToCache(line); - } - const s = this.lineCache.get(line); - return (s); - } - public getEndsInOperatorFromCache(line: number) { - const lineInCache = this.lineCache.has(line); - if (!lineInCache) { - this.addLineToCache(line); - } - const s = this.endsInOperatorCache.get(line); - return (s); - } - public addLineToCache(line: number) { + // Returns [Line, EndsInOperator] + public addLineToCache(line: number): [string, boolean] { const cleaned = cleanLine(this.getLine(line)); const endsInOperator = doesLineEndInOperator(cleaned); this.lineCache.set(line, cleaned); this.endsInOperatorCache.set(line, endsInOperator); + return [cleaned, endsInOperator]; + } + public getEndsInOperatorFromCache(line: number): boolean { + const lineInCache = this.endsInOperatorCache.get(line); + if (lineInCache === undefined) { + return this.addLineToCache(line)[1]; + } + return lineInCache; + } + public getLineFromCache(line: number): string { + const lineInCache = this.lineCache.get(line); + if (lineInCache === undefined) { + return this.addLineToCache(line)[0]; + } + return lineInCache; } } -function cleanLine(text: string) { - const cleaned = text.replace(/\s*\#.*/, ""); // Remove comments and preceeding spaces - return (cleaned); +function isQuote(c: string) { + return c === '"' || c === '\'' || c === '`'; +} + +function isComment(c: string) { + return c === '#'; +} + +export function cleanLine(text: string): string { + let cleaned = ''; + let withinQuotes = null; + for (let i = 0; i < text.length; i++) { + const c = text[i]; + if (isQuote(c)) { + if (withinQuotes === null) { + withinQuotes = c; + } else if (withinQuotes === c) { + withinQuotes = null; + } + } + if (isComment(c) && !withinQuotes) { + break; + } + + cleaned += c; + } + return (cleaned.trimEnd()); } function doesLineEndInOperator(text: string) { - const endingOperatorIndex = text.search(/(,|\+|!|\$|\^|&|\*|-|=|:|\'|~|\||\/|\?|%.*%)(\s*|\s*\#.*)$/); - const spacesOnlyIndex = text.search(/^\s*$/); // Space-only lines also counted. - return ((0 <= endingOperatorIndex) || (0 <= spacesOnlyIndex)); + const endingOperatorIndex = text.search(/(\(|,|\+|!|\$|\^|&|\*|-|=|:|~|\||\/|\?|<|>|%.*%)(\s*|\s*#.*)$/); + const spacesOnlyIndex = text.search(/^\s*$/); + + return ((endingOperatorIndex >= 0) || (spacesOnlyIndex >= 0)); } diff --git a/src/lintrConfig.ts b/src/lintrConfig.ts new file mode 100644 index 000000000..f48e6a4a2 --- /dev/null +++ b/src/lintrConfig.ts @@ -0,0 +1,29 @@ +'use strict'; + +import { existsSync, unlinkSync } from 'fs'; +import { join } from 'path'; +import { window } from 'vscode'; +import { executeRCommand, getCurrentWorkspaceFolder } from './util'; + +export async function createLintrConfig(): Promise { + const currentWorkspaceFolder = getCurrentWorkspaceFolder()?.uri.fsPath; + if (currentWorkspaceFolder === undefined) { + void window.showWarningMessage('Please open a workspace folder to create .lintr'); + return; + } + const lintrFilePath = join(currentWorkspaceFolder, '.lintr'); + if (existsSync(lintrFilePath)) { + const overwrite = await window.showWarningMessage( + '".lintr" file already exists. Do you want to overwrite?', + 'Yes', 'No' + ); + if (overwrite === 'No') { + return; + } + void unlinkSync(lintrFilePath); + } + return await executeRCommand(`lintr::use_lintr()`, currentWorkspaceFolder, (e: Error) => { + void window.showErrorMessage(e.message); + return ''; + }); +} diff --git a/src/liveShare/index.ts b/src/liveShare/index.ts new file mode 100644 index 000000000..e18305693 --- /dev/null +++ b/src/liveShare/index.ts @@ -0,0 +1,354 @@ +// re-exported variables +export * from './shareCommands'; +export * from './shareSession'; +export * from './shareTree'; +export * from './virtualDocs'; + +import * as vscode from 'vscode'; +import * as vsls from 'vsls'; +import * as fs from 'fs-extra'; + +import { enableSessionWatcher, extensionContext } from '../extension'; +import { attachActiveGuest, browserDisposables, initGuest } from './shareSession'; +import { initTreeView, rLiveShareProvider, shareWorkspace, ToggleNode } from './shareTree'; +import { Commands, Callback, liveShareOnRequest, liveShareRequest } from './shareCommands'; + +import { HelpFile } from '../helpViewer'; +import { WorkspaceData, workspaceData } from '../session'; +import { config } from '../util'; + +/// LiveShare +export let rHostService: HostService | undefined = undefined; +export let rGuestService: GuestService | undefined = undefined; +export let liveSession: vsls.LiveShare; +export let isGuestSession: boolean; +export let _sessionStatusBarItem: vscode.StatusBarItem; + +// service vars +export const ShareProviderName = 'vscode-r'; +export let service: vsls.SharedServiceProxy | vsls.SharedService | null = null; + +// random number to fake a UUID for differentiating between +// host calls and guest calls (specifically for the workspace +// viewer 'View' function) +export const UUID = Math.floor(Math.random() * Date.now()); + +/// state-tracking bools +// Bool to check if live share is loaded and active +export function isLiveShare(): boolean { + const shareStarted = liveSession?.session?.id; + // If there is a hosted session*, return true + // else return false + // * using vsls.getApi() instead of vsls.getApi().session.id + // * will always return true, even if a session is not active + // * (a session id will only exist if a session is active) + return !!shareStarted; +} + +export function isGuest(): boolean { + if (isLiveShare()) { + return liveSession.session.role === vsls.Role.Guest; + } else { + return false; + } +} + +export function isHost(): boolean { + if (isLiveShare()) { + return liveSession.session.role === vsls.Role.Host; + } else { + return false; + } +} + +// Initialises the Liveshare functionality for host & guest +// * session watcher is required * +export async function initLiveShare(context: vscode.ExtensionContext): Promise { + if (enableSessionWatcher) { + await LiveSessionListener(); + isGuestSession = isGuest(); + if (!isGuestSession) { + // Construct tree view for host + initTreeView(); + } else { + // Construct guest session watcher + initGuest(context); + } + + // Set context value for hiding buttons for guests + void vscode.commands.executeCommand('setContext', 'r.liveShare:isGuest', isGuestSession); + + // push commands + if (!isGuestSession) { + context.subscriptions.push( + vscode.commands.registerCommand( + 'r.liveShare.toggle', (node: ToggleNode) => node.toggle(rLiveShareProvider) + ), + vscode.commands.registerCommand( + 'r.liveShare.retry', async () => { + await LiveSessionListener(); + rLiveShareProvider.refresh(); + } + ) + ); + } else { + context.subscriptions.push( + vscode.commands.registerCommand('r.attachActiveGuest', () => attachActiveGuest()) + ); + } + } +} + +// Listens for the activation of a LiveShare session +export async function LiveSessionListener(): Promise { + rHostService = new HostService; + rGuestService = new GuestService; + + // catch errors in case of issues with the + // LiveShare extension/API (see #671) + async function tryAPI(): Promise { + try { + return await Promise.race([ + vsls.getApi(), + new Promise((res) => setTimeout(() => res(null), config().get('liveShare.timeout'))) + ]); + } catch(e: unknown) { + console.log('[LiveSessionListener] an error occured when attempting to access the Live Share API.', e); + return null; + } + } + + // Return out when the vsls extension isn't + // installed/available + const liveSessionStatus = await tryAPI(); + + void vscode.commands.executeCommand('setContext', 'r.liveShare:aborted', !liveSessionStatus); + + if (!liveSessionStatus) { + console.log('[LiveSessionListener] aborted'); + return; + } + + liveSession = liveSessionStatus as vsls.LiveShare; + console.log('[LiveSessionListener] started'); + + // When the session state changes, attempt to + // start a liveSession service, which is responsible + // for providing session-watcher functionality + // to guest sessions + liveSession.onDidChangeSession(async (e: vsls.SessionChangeEvent) => { + switch (e.session.role) { + case vsls.Role.None: + console.log('[LiveSessionListener] end event'); + await sessionCleanup(); + break; + case vsls.Role.Guest: + console.log('[LiveSessionListener] guest event'); + await rGuestService?.startService(); + break; + case vsls.Role.Host: + console.log('[LiveSessionListener] host event'); + await rHostService?.startService(); + rLiveShareProvider.refresh(); + break; + default: + console.log('[LiveSessionListener] default case'); + break; + } + }, null, extensionContext.subscriptions); + + // onDidChangeSession seems to only activate when the host joins/leaves, + // or roles are changed somehow - may be a regression in API, + // this is a workaround for the time being + switch (liveSession.session.role) { + case vsls.Role.None: + break; + case vsls.Role.Guest: + console.log('[LiveSessionListener] guest event'); + await rGuestService.startService(); + break; + default: + console.log('[LiveSessionListener] host event'); + await rHostService.startService(); + break; + } +} + +// Communication between the HostService and the GuestService +// typically falls under 2 communication paths (there are exceptions): +// +// 1. a function on the HostService is called, which pushes +// an event (notify), which is picked up by a callback (onNotify) +// e.g. rHostService.notifyRequest +// +// 2. a function on the GuestService is called, which pushes a +// request to the HostService, which is picked up the HostService +// callback and * returned * to the GuestService +// e.g. rGuestService.requestFileContent +// +// Note: If you are wanting the guest/host to run code, you must either ensure that +// the code is accessible from the guest/host, or the guest/host is notified of the +// method by the other role. Calling, for instance, a GuestService method from +// a method only accessible to the host will NOT call the method for the guest. +export class HostService { + private _isStarted: boolean = false; + // Service state getter + public isStarted(): boolean { + return this._isStarted; + } + public async startService(): Promise { + // Provides core liveshare functionality + // The shared service is used as a RPC service + // to pass messages between the host and guests + service = await liveSession.shareService(ShareProviderName); + if (service) { + this._isStarted = true; + for (const command in Commands.host) { + void liveShareOnRequest(command, Commands.host[command], service); + console.log(`[HostService] added ${command} callback`); + } + } else { + console.error('[HostService] service activation failed'); + } + } + public async stopService(): Promise { + await liveSession.unshareService(ShareProviderName); + service = null; + this._isStarted = false; + } + /// Session Syncing /// + // These are called from the host in order to tell the guest session + // to update the env/request/plot + // This way, we don't have to re-create a guest version of the session + // watcher, and can rely on the host to tell when something needs to be + // updated + public notifyWorkspace(hostWorkspace: WorkspaceData): void { + if (this._isStarted && shareWorkspace) { + void liveShareRequest(Callback.NotifyWorkspaceUpdate, hostWorkspace); + } + } + public notifyRequest(file: string, force: boolean = false): void { + if (this._isStarted && shareWorkspace) { + void liveShareRequest(Callback.NotifyRequestUpdate, file, force); + void this.notifyWorkspace(workspaceData); + } + } + public notifyPlot(file: string): void { + if (this._isStarted && shareWorkspace) { + void liveShareRequest(Callback.NotifyPlotUpdate, file); + } + } + public notifyGuestPlotManager(url: string): void { + if (this._isStarted) { + void liveShareRequest(Callback.NotifyGuestPlotManager, url); + } + } + public orderGuestDetach(): void { + if (this._isStarted) { + void liveShareRequest(Callback.OrderDetach); + } + } +} + +export class GuestService { + private _isStarted: boolean = false; + public isStarted(): boolean { + return this._isStarted; + } + public async startService(): Promise { + service = await liveSession.getSharedService(ShareProviderName); + if (service) { + this._isStarted = true; + this.requestAttach(); + for (const command in Commands.guest) { + void liveShareOnRequest(command, Commands.guest[command], service); + console.log(`[GuestService] added ${command} callback`); + } + } else { + console.error('[GuestService] service request failed'); + } + } + public setStatusBarItem(sessionStatusBarItem: vscode.StatusBarItem): void { + _sessionStatusBarItem = sessionStatusBarItem; + } + // The guest requests the host returns the attach specifications to the guest + // This ensures that guests without read/write access can still view the + // R workspace + public requestAttach(): void { + if (this._isStarted) { + void liveShareRequest(Callback.RequestAttachGuest); + // focus guest term if it exists + const rTermNameOptions = ['R [Shared]', 'R Interactive [Shared]']; + const activeTerminalName = vscode.window.activeTerminal?.name; + if (activeTerminalName && !rTermNameOptions.includes(activeTerminalName)) { + for (const [i] of vscode.window.terminals.entries()) { + const terminal = vscode.window.terminals[i]; + const terminalName = terminal.name; + if (rTermNameOptions.includes(terminalName)) { + terminal.show(true); + } + } + } + } + } + // Used to ensure that the guest can run workspace viewer commands + // e.g.view, remove, clean + // * Permissions are handled host-side + public requestRunTextInTerm(text: string): void { + if (this._isStarted) { + void liveShareRequest(Callback.RequestRunTextInTerm, text); + } + } + // The session watcher relies on files for providing many functions to vscode-R. + // As LiveShare does not allow for exposing files outside a given workspace, + // the guest must rely on the host sending the content of a given file, in place + // of having their own /tmp/ files + public async requestFileContent(file: fs.PathLike | number): Promise; + public async requestFileContent(file: fs.PathLike | number, encoding: string): Promise; + public async requestFileContent(file: fs.PathLike | number, encoding?: string): Promise { + if (this._isStarted) { + if (encoding !== undefined) { + const content: string | unknown = await liveShareRequest(Callback.GetFileContent, file, encoding); + if (typeof content === 'string') { + return content; + } else { + console.error('[GuestService] failed to retrieve file content (not of type "string")'); + } + } else { + const content: Buffer | unknown = await liveShareRequest(Callback.GetFileContent, file); + if (content) { + return content as Buffer; + } else { + console.error('[GuestService] failed to retrieve file content (not of type "Buffer")'); + } + } + } + } + + public async requestHelpContent(file: string): Promise { + const content: string | null | unknown = await liveShareRequest(Callback.GetHelpFileContent, file); + if (content) { + return content as HelpFile; + } else { + console.error('[GuestService] failed to retrieve help content from host'); + } + } + +} + +// Clear up any listeners & disposables, so that vscode-R +// isn't slowed down if liveshare is ended +// This is used instead of relying on context disposables, +// as an R session can continue even when liveshare is ended +async function sessionCleanup(): Promise { + if (rHostService?.isStarted()) { + console.log('[HostService] stopping service'); + await rHostService.stopService(); + for (const [key, item] of browserDisposables.entries()) { + console.log(`[HostService] disposing of browser ${item.url}`); + item.Disposable.dispose(); + browserDisposables.splice(key); + } + rLiveShareProvider.refresh(); + } +} diff --git a/src/liveShare/shareCommands.ts b/src/liveShare/shareCommands.ts new file mode 100644 index 000000000..7745d2afd --- /dev/null +++ b/src/liveShare/shareCommands.ts @@ -0,0 +1,161 @@ +import * as vsls from 'vsls'; +import * as vscode from 'vscode'; +import * as fs from 'fs-extra'; + +import { rHostService, isGuest, service } from '.'; +import { updateGuestRequest, updateGuestWorkspace, updateGuestPlot, detachGuest } from './shareSession'; +import { forwardCommands, shareWorkspace } from './shareTree'; + +import { runTextInTerm } from '../rTerminal'; +import { requestFile, WorkspaceData } from '../session'; +import { HelpFile } from '../helpViewer'; +import { globalHttpgdManager, globalRHelp } from '../extension'; + +// used in sending messages to the guest service, +// distinguishes the type of vscode message to show +const enum MessageType { + information = 'information', + error = 'error', + warning = 'warning' +} + +interface ICommands { + host: { + [name: string]: unknown + }, + guest: { + [name: string]: unknown + } +} + +// used for notify & request events +// (mainly to prevent typos) +export const enum Callback { + NotifyWorkspaceUpdate = 'NotifyWorkspaceUpdate', + NotifyPlotUpdate = 'NotifyPlotUpdate', + NotifyRequestUpdate = 'NotifyRequestUpdate', + NotifyMessage = 'NotifyMessage', + RequestAttachGuest = 'RequestAttachGuest', + RequestRunTextInTerm = 'RequestRunTextInTerm', + GetFileContent = 'GetFileContent', + OrderDetach = 'OrderDetach', + GetHelpFileContent = 'GetHelpFileContent', + NotifyGuestPlotManager = 'NotifyGuestPlotManager' +} + +// To contribute a request between the host and guest, +// add the method that will be triggered with the callback. +// method arguments should be defined as an array of 'args' +// +// A response should have the this typical structure: +// [Callback.name]: (args:[]): returnType => { +// method +// } +// +// A request, by comparison, may look something like this: +// method(args) { +// await request(Callback.name, args) +// } +export const Commands: ICommands = { + 'host': { + /// Terminal commands /// + // Command arguments are sent from the guest to the host, + // and then the host sends the arguments to the console + [Callback.RequestAttachGuest]: (): void => { + if (shareWorkspace && rHostService) { + void rHostService.notifyRequest(requestFile, true); + } else { + void liveShareRequest(Callback.NotifyMessage, 'The host has not enabled guest attach.', MessageType.warning); + } + }, + [Callback.RequestRunTextInTerm]: (args: [text: string]): void => { + if (forwardCommands) { + void runTextInTerm(`${args[0]}`); + } else { + void liveShareRequest(Callback.NotifyMessage, 'The host has not enabled command forwarding. Command was not sent.', MessageType.warning); + } + + }, + [Callback.GetHelpFileContent]: (args: [text: string]): Promise | undefined => { + return globalRHelp?.getHelpFileForPath(args[0]); + }, + /// File Handling /// + // Host reads content from file, then passes the content + // to the guest session. + [Callback.GetFileContent]: async (args: [text: string, encoding?: string]): Promise => { + return args[1] !== undefined ? + await fs.readFile(args[0], args[1]) : + await fs.readFile(args[0]); + } + }, + 'guest': { + [Callback.NotifyRequestUpdate]: (args: [file: string, force: boolean]): void => { + void updateGuestRequest(args[0], args[1]); + }, + [Callback.NotifyWorkspaceUpdate]: (args: [hostWorkspace: WorkspaceData]): void => { + void updateGuestWorkspace(args[0]); + }, + [Callback.NotifyPlotUpdate]: (args: [file: string]): void => { + void updateGuestPlot(args[0]); + }, + [Callback.NotifyGuestPlotManager]: (args: [url: string]): void => { + void globalHttpgdManager?.showViewer(args[0]); + }, + [Callback.OrderDetach]: (): void => { + void detachGuest(); + }, + /// vscode Messages /// + // The host sends messages to the guest, which are displayed as a vscode window message + // E.g., teling the guest a terminal is not attached to the current session + // This way, we don't have to do much error checking on the guests side, which is more secure + // and less prone to error + [Callback.NotifyMessage]: (args: [text: string, messageType: MessageType]): void => { + switch (args[1]) { + case MessageType.error: + return void vscode.window.showErrorMessage(args[0]); + case MessageType.information: + return void vscode.window.showInformationMessage(args[0]); + case MessageType.warning: + return void vscode.window.showWarningMessage(args[0]); + case undefined: + return void vscode.window.showInformationMessage(args[0]); + } + } + } +}; + + +// The following onRequest and request methods are wrappers +// around the vsls RPC API. These are intended to simplify +// the API, so that the learning curve is minimal for contributing +// future callbacks. +// +// You can see that the onNotify and notify methods have been +// aggregated under these two methods. This is because the host service +// has no request methods, and for *most* purposes, there is little functional +// difference between request and notify. +export function liveShareOnRequest(name: string, command: unknown, service: vsls.SharedService | vsls.SharedServiceProxy | null): void { + if (isGuest()) { + // is guest service + (service as vsls.SharedServiceProxy).onNotify(name, command as vsls.NotifyHandler); + } else { + // is host service + (service as vsls.SharedService).onRequest(name, command as vsls.RequestHandler); + } +} + +export function liveShareRequest(name: string, ...rest: unknown[]): unknown { + if (isGuest()) { + if (rest !== undefined) { + return (service as vsls.SharedServiceProxy).request(name, rest); + } else { + return (service as vsls.SharedServiceProxy).request(name, []); + } + } else { + if (rest !== undefined) { + return (service as vsls.SharedService).notify(name, { ...rest }); + } else { + return (service as vsls.SharedService).notify(name, {}); + } + } +} diff --git a/src/liveShare/shareSession.ts b/src/liveShare/shareSession.ts new file mode 100644 index 000000000..d11b930e0 --- /dev/null +++ b/src/liveShare/shareSession.ts @@ -0,0 +1,276 @@ +import path = require('path'); +import * as vscode from 'vscode'; + +import { extensionContext, globalHttpgdManager, globalRHelp, rWorkspace } from '../extension'; +import { asViewColumn, config, readContent } from '../util'; +import { showBrowser, showDataView, showWebView, WorkspaceData } from '../session'; +import { liveSession, UUID, rGuestService, _sessionStatusBarItem as sessionStatusBarItem } from '.'; +import { autoShareBrowser } from './shareTree'; +import { docProvider, docScheme } from './virtualDocs'; + +// Workspace Vars +let guestPid: string; +export let guestWorkspace: WorkspaceData | undefined; +export let guestResDir: string; +let rVer: string; +let info: IRequest['info']; + +// Browser Vars +// Used to keep track of shared browsers +export const browserDisposables: { Disposable: vscode.Disposable, url: string, name: string }[] = []; + +export interface IRequest { + command: string; + time?: string; + pid?: string; + wd?: string; + source?: string; + type?: string; + title?: string; + file?: string; + viewer?: string; + plot?: string; + action?: string; + args?: unknown; + sd?: string; + url?: string; + requestPath?: string; + uuid?: number; + tempdir?: string; + version?: string; + info?: { + version: string, + command: string, + start_time: string + }; +} + +export function initGuest(context: vscode.ExtensionContext): void { + // create status bar item that contains info about the *guest* session watcher + console.info('Create guestSessionStatusBarItem'); + const sessionStatusBarItem = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Right, 1000); + sessionStatusBarItem.command = 'r.attachActiveGuest'; + sessionStatusBarItem.text = 'Guest R: (not attached)'; + sessionStatusBarItem.tooltip = 'Click to attach to host terminal'; + sessionStatusBarItem.show(); + context.subscriptions.push( + sessionStatusBarItem, + vscode.workspace.registerTextDocumentContentProvider(docScheme, docProvider) + ); + rGuestService?.setStatusBarItem(sessionStatusBarItem); + guestResDir = path.join(context.extensionPath, 'dist', 'resources'); +} + +export function detachGuest(): void { + console.info('[Guest Service] detach guest from workspace'); + sessionStatusBarItem.text = 'Guest R: (not attached)'; + sessionStatusBarItem.tooltip = 'Click to attach to host terminal'; + guestWorkspace = undefined; + rWorkspace?.refresh(); +} + +export function attachActiveGuest(): void { + if (config().get('sessionWatcher', true)) { + console.info('[attachActiveGuest]'); + void rGuestService?.requestAttach(); + } else { + void vscode.window.showInformationMessage('This command requires that r.sessionWatcher be enabled.'); + } +} + +// Guest version of session.ts updateRequest(), no need to check for changes in files +// as this is handled by the session.ts variant +// the force parameter is used for ensuring that the 'attach' case is appropriately called on guest join +export async function updateGuestRequest(file: string, force: boolean = false): Promise { + const requestContent: string | undefined = await readContent(file, 'utf8'); + if (!requestContent) { + return; + } + console.info(`[updateGuestRequest] request: ${requestContent}`); + if (typeof (requestContent) !== 'string') { + return; + } + + const request: IRequest = JSON.parse(requestContent) as IRequest; + if (!request) { + return; + } + + if (force) { + // The last request is not necessarily an attach request. + guestPid = String(request.pid); + console.info(`[updateGuestRequest] attach PID: ${guestPid}`); + sessionStatusBarItem.text = `Guest R: ${guestPid}`; + sessionStatusBarItem.tooltip = 'Click to attach to host terminal.'; + sessionStatusBarItem.show(); + } + + if (request.uuid === null || request.uuid === undefined || request.uuid === UUID) { + switch (request.command) { + case 'help': { + if (globalRHelp) { + console.log(request.requestPath); + if (request.requestPath) { + await globalRHelp.showHelpForPath(request.requestPath, request.viewer); + } + } + break; + } + case 'httpgd': { + if (request.url) { + await globalHttpgdManager?.showViewer(request.url); + } + break; + } + case 'attach': { + guestPid = String(request.pid); + rVer = String(request.version); + info = request.info; + console.info(`[updateGuestRequest] attach PID: ${guestPid}`); + sessionStatusBarItem.text = `Guest R ${rVer}: ${guestPid}`; + // eslint-disable-next-line @typescript-eslint/restrict-template-expressions + sessionStatusBarItem.tooltip = `${info?.version || 'unknown version'}\nProcess ID: ${guestPid}\nCommand: ${info?.command}\nStart time: ${info?.start_time}\nClick to attach to host terminal.`; + sessionStatusBarItem.show(); + break; + } + case 'browser': { + if (request.url && request.title && request.viewer !== undefined) { + await showBrowser(request.url, request.title, request.viewer); + } + break; + } + case 'webview': { + if (request.file && request.title && request.viewer !== undefined) { + await showWebView(request.file, request.title, request.viewer); + } + break; + } + case 'dataview': { + if (request.source && request.type && request.title && request.file + && request.viewer !== undefined) { + await showDataView(request.source, + request.type, request.title, request.file, request.viewer); + } + break; + } + case 'rstudioapi': { + console.error(`[GuestService] ${request.command} not supported`); + break; + } + default: + console.error(`[updateRequest] Unsupported command: ${request.command}`); + } + + } +} + +// Call from host, pass parsed workspace file +export function updateGuestWorkspace(hostWorkspace: WorkspaceData): void { + if (hostWorkspace) { + guestWorkspace = hostWorkspace; + void rWorkspace?.refresh(); + console.info('[updateGuestWorkspace] Done'); + } +} + +// Instead of creating a file, we pass the base64 of the plot image +// to the guest, and read that into an html page +let panel: vscode.WebviewPanel | undefined = undefined; +export async function updateGuestPlot(file: string): Promise { + const plotContent = await readContent(file, 'base64'); + // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment + + const guestPlotView: vscode.ViewColumn = asViewColumn(config().get('session.viewers.viewColumn.plot'), vscode.ViewColumn.Two); + if (plotContent) { + if (panel) { + panel.webview.html = getGuestImageHtml(plotContent); + panel.reveal(guestPlotView, true); + } else { + panel = vscode.window.createWebviewPanel('dataview', 'R Guest Plot', + { + preserveFocus: true, + viewColumn: guestPlotView, + }, + { + enableScripts: true, + enableFindWidget: true, + retainContextWhenHidden: true, + localResourceRoots: [vscode.Uri.file(guestResDir)], + }); + const content = getGuestImageHtml(plotContent); + panel.webview.html = content; + panel.onDidDispose( + () => { + panel = undefined; + }, + undefined, + extensionContext.subscriptions + ); + } + } +} + + +// Purely used in order to decode a base64 string into +// an image format, bypassing saving a file onto the guest's system +function getGuestImageHtml(content: string) { + return ` + + + + + + + + + + + +`; +} + +export async function shareServer(url: URL, name: string): Promise { + return liveSession.shareServer({ + port: parseInt(url.port), + displayName: `${name} (${url.host})`, + browseUrl: url.toString() + }); +} + +// Share and close browser are called from the +// host session +// Automates sharing browser sessions through the +// shareServer method +export async function shareBrowser(url: string, name: string, force: boolean = false): Promise { + if (autoShareBrowser || force) { + const _url = new URL(url); + const disposable = await shareServer(_url, name); + console.log(`[HostService] shared ${name} at ${url}`); + browserDisposables.push({ Disposable: disposable, url, name }); + } +} + +export function closeBrowser(url: string): void { + browserDisposables.find( + e => e.url === url + )?.Disposable.dispose(); + + for (const [key, item] of browserDisposables.entries()) { + if (item.url === url) { + browserDisposables.splice(key, 1); + } + } +} diff --git a/src/liveShare/shareTree.ts b/src/liveShare/shareTree.ts new file mode 100644 index 000000000..be0941f99 --- /dev/null +++ b/src/liveShare/shareTree.ts @@ -0,0 +1,158 @@ +import * as vscode from 'vscode'; +import { requestFile } from '../session'; + +import { config } from '../util'; +import { isLiveShare, rHostService } from '.'; + +export let forwardCommands: boolean; +export let shareWorkspace: boolean; +export let autoShareBrowser: boolean; +export let rLiveShareProvider: LiveShareTreeProvider; + +export function initTreeView(): void { + // get default bool values from settings + shareWorkspace = config().get('liveShare.defaults.shareWorkspace', true); + forwardCommands = config().get('liveShare.defaults.commandForward', false); + autoShareBrowser = config().get('liveShare.defaults.shareBrowser', false); + + // create tree view for host controls + rLiveShareProvider = new LiveShareTreeProvider(); + void vscode.window.registerTreeDataProvider( + 'rLiveShare', + rLiveShareProvider + ); +} + +export class LiveShareTreeProvider implements vscode.TreeDataProvider { + private _onDidChangeTreeData: vscode.EventEmitter = new vscode.EventEmitter(); + readonly onDidChangeTreeData: vscode.Event = this._onDidChangeTreeData.event; + + refresh(): void { + this._onDidChangeTreeData.fire(); + } + + getTreeItem(element: Node): vscode.TreeItem { + return element; + } + + // If a node needs to be collapsible, + // change the element condition & return value + getChildren(element?: Node): Node[] | undefined { + if (element) { + return; + } else { + return this.getNodes(); + } + } + + // To add a tree item to the LiveShare R view, + // write a class object that extends Node and + // add it to the list of nodes here + private getNodes(): Node[] | undefined { + let items: Node[] | undefined = undefined; + if (isLiveShare()) { + items = [ + new ShareNode(), + new CommandNode(), + new PortNode() + ]; + } + + return items; + } +} + +// Base class for adding to +abstract class Node extends vscode.TreeItem { + declare public label?: string; + declare public tooltip?: string; + declare public contextValue?: string; + declare public description?: string; + declare public iconPath?: vscode.ThemeIcon; + declare public collapsibleState?: vscode.TreeItemCollapsibleState; + + constructor() { + super(''); + } +} + +// Class for any tree item that should have a toggleable state +// To implement a ToggleNode, in the super, provide a boolean +// that is used for tracking state. +// If a toggle is not required, extend a different Node type. +export abstract class ToggleNode extends Node { + public toggle(treeProvider: LiveShareTreeProvider): void { treeProvider.refresh(); } + declare public label?: string; + declare public tooltip?: string; + declare public contextValue?: string; + declare public description?: string; + declare public iconPath?: vscode.ThemeIcon; + declare public collapsibleState?: vscode.TreeItemCollapsibleState; + + constructor(bool: boolean) { + super(); + this.description = bool === true ? 'Enabled' : 'Disabled'; + } + +} + +/// Nodes for changing R LiveShare variables +class ShareNode extends ToggleNode { + toggle(treeProvider: LiveShareTreeProvider): void { + shareWorkspace = !shareWorkspace; + this.description = shareWorkspace === true ? 'Enabled' : 'Disabled'; + if (shareWorkspace) { + void rHostService?.notifyRequest(requestFile, true); + } else { + void rHostService?.orderGuestDetach(); + } + treeProvider.refresh(); + } + + public label: string = 'Share R Workspace'; + public tooltip: string = 'Whether guests can access the current R session and its workspace'; + public contextValue: string = 'shareNode'; + declare public description?: string; + public iconPath: vscode.ThemeIcon = new vscode.ThemeIcon('broadcast'); + public collapsibleState: vscode.TreeItemCollapsibleState = vscode.TreeItemCollapsibleState.None; + + constructor() { + super(shareWorkspace); + } +} + +class CommandNode extends ToggleNode { + toggle(treeProvider: LiveShareTreeProvider): void { + forwardCommands = !forwardCommands; + this.description = forwardCommands === true ? 'Enabled' : 'Disabled'; + treeProvider.refresh(); + } + + public label: string = 'Guest interaction with host R extension'; + public tooltip: string = 'Whether commands to interact with the R extension should be forwarded from the guest to the host (bypasses permissions); shared R terminal (command line) permissions can be toggled in the Live Share extension'; + public contextValue: string = 'commandNode'; + public iconPath: vscode.ThemeIcon = new vscode.ThemeIcon('debug-step-over'); + public collapsibleState: vscode.TreeItemCollapsibleState = vscode.TreeItemCollapsibleState.None; + + constructor() { + super(forwardCommands); + } +} + +class PortNode extends ToggleNode { + toggle(treeProvider: LiveShareTreeProvider): void { + autoShareBrowser = !autoShareBrowser; + this.description = autoShareBrowser === true ? 'Enabled' : 'Disabled'; + treeProvider.refresh(); + } + + public label: string = 'Auto share ports'; + public tooltip: string = 'Whether opened R browsers should be shared with guests'; + public contextValue: string = 'portNode'; + public iconPath: vscode.ThemeIcon = new vscode.ThemeIcon('plug'); + public collapsibleState: vscode.TreeItemCollapsibleState = vscode.TreeItemCollapsibleState.None; + + constructor() { + super(autoShareBrowser); + } +} diff --git a/src/liveShare/virtualDocs.ts b/src/liveShare/virtualDocs.ts new file mode 100644 index 000000000..6d12d67dd --- /dev/null +++ b/src/liveShare/virtualDocs.ts @@ -0,0 +1,21 @@ +import * as vscode from 'vscode'; + +export const docScheme = 'vscode-r'; +export const docProvider = new class implements vscode.TextDocumentContentProvider { + // class can be expanded if needed + provideTextDocumentContent(uri: vscode.Uri): string | Thenable { + return uri.query; + } +}; + +export async function openVirtualDoc(file: string, content: string, preserveFocus: boolean, preview: boolean, viewColumn: number): Promise { + if (content) { + const uri = vscode.Uri.parse(`${docScheme}:${file}?${content}`); + const doc = await vscode.workspace.openTextDocument(uri); + await vscode.window.showTextDocument(doc, { + preserveFocus: preserveFocus, + preview: preview, + viewColumn: viewColumn + }); + } +} \ No newline at end of file diff --git a/src/plotViewer/httpgdTypes.d.ts b/src/plotViewer/httpgdTypes.d.ts new file mode 100644 index 000000000..9d713eae0 --- /dev/null +++ b/src/plotViewer/httpgdTypes.d.ts @@ -0,0 +1,88 @@ + + +import { Httpgd } from 'httpgd'; +import { HttpgdPlotId } from 'httpgd/lib/types'; +import * as vscode from 'vscode'; +import { HttpgdManager } from '.'; +import { PreviewPlotLayout } from './webviewMessages'; + +export type MaybePromise = T | Promise; + +export interface HttpgdPlot { + + // unique ID for this plot (w.r.t. this connection/device) + id: HttpgdPlotId; + + // data of the plot + data: T; + + // Size + height: number; + width: number; + zoom: number; +} + +// Example for possible viewer creation options: +export interface HttpgdViewerOptions { + parent: HttpgdManager; + token?: string; + preserveFocus?: boolean; + viewColumn?: vscode.ViewColumn; + htmlRoot: string; + stripStyles?: boolean; + fullWindow?: boolean; + previewPlotLayout?: PreviewPlotLayout, + resizeTimeoutLength?: number; + refreshTimeoutLength?: number; +} + +// Roughly combines the functionality of HttpgdNavigator and HttpgdViewer +export class IHttpgdViewer { + // Actual webview where the plot viewer is shown + // Will have to be created anew, if the user closes it and the plot changes + webviewPanel?: vscode.WebviewPanel; + + // Api that provides plot contents etc. + api: Httpgd; + + // active plots + plots: HttpgdPlot[]; + + // Id of the currently viewed plot + activePlot?: HttpgdPlotId; + + // Size of the view area: + viewHeight: number; + viewWidth: number; + + // constructor called by the session watcher if a corresponding function was called in R + // creates a new api instance itself + constructor(options: HttpgdViewerOptions); + + + // Methods to interact with the webview + // Can e.g. be called by vscode commands + menu items: + + // Called to create a new webview if the user closed the old one: + show(preserveFocus?: boolean): void; + + // focus a specific plot id + focusPlot(id: HttpgdPlotId): void; + + // navigate through plots (supply `true` to go to end/beginning of list) + nextPlot(last?: boolean): void; + prevPlot(first?: boolean): void; + + // restore closed plots, show most recent plot etc.? + resetPlots(): void; + + // export plot + // if no format supplied, show a quickpick menu etc. + // if no filename supplied, show selector window + exportPlot(id: HttpgdPlotId, format?: string, outFile?: string): void; + + // Dispose-function to clean up when vscode closes + // E.g. to close connections etc., notify R, ... + // Not sure if sensible here + dispose?(): void; +} diff --git a/src/plotViewer/index.ts b/src/plotViewer/index.ts new file mode 100644 index 000000000..20f81d636 --- /dev/null +++ b/src/plotViewer/index.ts @@ -0,0 +1,861 @@ +/* eslint-disable @typescript-eslint/no-unsafe-argument */ +/* eslint-disable @typescript-eslint/no-unsafe-assignment */ +/* eslint-disable @typescript-eslint/no-explicit-any */ + + +import * as vscode from 'vscode'; +import { Httpgd } from 'httpgd'; +import { HttpgdPlot, IHttpgdViewer, HttpgdViewerOptions } from './httpgdTypes'; +import * as path from 'path'; +import * as fs from 'fs'; +import * as ejs from 'ejs'; + +import { asViewColumn, config, setContext, UriIcon, makeWebviewCommandUriString } from '../util'; + +import { extensionContext } from '../extension'; + +import { FocusPlotMessage, InMessage, OutMessage, ToggleStyleMessage, UpdatePlotMessage, HidePlotMessage, AddPlotMessage, PreviewPlotLayout, PreviewPlotLayoutMessage, ToggleFullWindowMessage } from './webviewMessages'; +import { HttpgdIdResponse, HttpgdPlotId, HttpgdRendererId } from 'httpgd/lib/types'; +import { Response } from 'node-fetch'; +import { autoShareBrowser, isHost, shareServer } from '../liveShare'; + +const commands = [ + 'showViewers', + 'openUrl', + 'openExternal', + 'showIndex', + 'toggleStyle', + 'toggleFullWindow', + 'togglePreviewPlots', + 'exportPlot', + 'nextPlot', + 'prevPlot', + 'lastPlot', + 'firstPlot', + 'hidePlot', + 'closePlot', + 'resetPlots', + 'zoomIn', + 'zoomOut' +] as const; + +type CommandName = typeof commands[number]; + +export function initializeHttpgd(): HttpgdManager { + const httpgdManager = new HttpgdManager(); + for (const cmd of commands) { + const fullCommand = `r.plot.${cmd}`; + const cb = httpgdManager.getCommandHandler(cmd); + extensionContext.subscriptions.push( + vscode.commands.registerCommand(fullCommand, cb) + ); + } + return httpgdManager; +} + +export class HttpgdManager { + viewers: HttpgdViewer[] = []; + + viewerOptions: HttpgdViewerOptions; + + recentlyActiveViewers: HttpgdViewer[] = []; + + constructor() { + const htmlRoot = extensionContext.asAbsolutePath('html/httpgd'); + this.viewerOptions = { + parent: this, + htmlRoot: htmlRoot, + preserveFocus: true + }; + } + + public async showViewer(urlString: string): Promise { + const url = new URL(urlString); + const host = url.host; + const token = url.searchParams.get('token') || undefined; + const ind = this.viewers.findIndex( + (viewer) => viewer.host === host + ); + if (ind >= 0) { + const viewer = this.viewers.splice(ind, 1)[0]; + this.viewers.unshift(viewer); + viewer.show(); + } else { + const conf = config(); + const colorTheme = conf.get('plot.defaults.colorTheme', 'vscode'); + this.viewerOptions.stripStyles = (colorTheme === 'vscode'); + this.viewerOptions.previewPlotLayout = conf.get('plot.defaults.plotPreviewLayout', 'multirow'); + this.viewerOptions.refreshTimeoutLength = conf.get('plot.timing.refreshInterval', 10); + this.viewerOptions.resizeTimeoutLength = conf.get('plot.timing.resizeInterval', 100); + this.viewerOptions.fullWindow = conf.get('plot.defaults.fullWindowMode', false); + this.viewerOptions.token = token; + const viewer = new HttpgdViewer(host, this.viewerOptions); + if (isHost() && autoShareBrowser) { + const disposable = await shareServer(url, 'httpgd'); + viewer.webviewPanel?.onDidDispose(() => void disposable.dispose()); + } + this.viewers.unshift(viewer); + } + } + + public registerActiveViewer(viewer: HttpgdViewer): void { + const ind = this.recentlyActiveViewers.indexOf(viewer); + if (ind) { + this.recentlyActiveViewers.splice(ind, 1); + } + this.recentlyActiveViewers.unshift(viewer); + } + + public getRecentViewer(): HttpgdViewer | undefined { + return this.recentlyActiveViewers.find((viewer) => !!viewer.webviewPanel); + } + + public getNewestViewer(): HttpgdViewer | undefined { + return this.viewers[0]; + } + + public getCommandHandler(command: CommandName): (...args: any[]) => void { + return (...args: any[]) => { + this.handleCommand(command, ...args); + }; + } + + public async openUrl(): Promise { + const clipText = await vscode.env.clipboard.readText(); + const val0 = clipText.trim().split(/[\n ]/)[0]; + const options: vscode.InputBoxOptions = { + value: val0, + prompt: 'Please enter the httpgd url' + }; + const urlString = await vscode.window.showInputBox(options); + if (urlString) { + await this.showViewer(urlString); + } + } + + // generic command handler + public handleCommand(command: CommandName, hostOrWebviewUri?: string | vscode.Uri, ...args: any[]): void { + // the number and type of arguments given to a command can vary, depending on where it was called from: + // - calling from the title bar menu provides two arguments, the first of which identifies the webview + // - calling from the command palette provides no arguments + // - calling from a command uri provides a flexible number/type of arguments + // below is an attempt to handle these different combinations efficiently and (somewhat) robustly + // + + if (command === 'showViewers') { + this.viewers.forEach(viewer => { + viewer.show(true); + }); + return; + } else if (command === 'openUrl') { + void this.openUrl(); + return; + } + + // Identify the correct viewer + let viewer: HttpgdViewer | undefined; + if (typeof hostOrWebviewUri === 'string') { + const host = hostOrWebviewUri; + viewer = this.viewers.find((viewer) => viewer.host === host); + } else if (hostOrWebviewUri instanceof vscode.Uri) { + const uri = hostOrWebviewUri; + viewer = this.viewers.find((viewer) => viewer.getPanelPath() === uri.path); + } + + // fall back to most recent viewer + viewer ||= this.getRecentViewer(); + + // Abort if no viewer identified + if (!viewer) { + return; + } + + // Get possible arguments for commands: + const stringArg = findItemOfType(args, 'string'); + const boolArg = findItemOfType(args, 'boolean'); + + // Call corresponding method, possibly with an argument: + switch (command) { + case 'showIndex': { + void viewer.focusPlot(stringArg); + break; + } case 'nextPlot': { + void viewer.nextPlot(boolArg); + break; + } case 'prevPlot': { + void viewer.prevPlot(boolArg); + break; + } case 'lastPlot': { + void viewer.nextPlot(true); + break; + } case 'firstPlot': { + void viewer.prevPlot(true); + break; + } case 'resetPlots': { + viewer.resetPlots(); + break; + } case 'toggleStyle': { + void viewer.toggleStyle(boolArg); + break; + } case 'togglePreviewPlots': { + void viewer.togglePreviewPlots(stringArg as PreviewPlotLayout); + break; + } case 'closePlot': { + void viewer.closePlot(stringArg); + break; + } case 'hidePlot': { + void viewer.hidePlot(stringArg); + break; + } case 'exportPlot': { + void viewer.exportPlot(stringArg); + break; + } case 'zoomIn': { + void viewer.zoomIn(); + break; + } case 'zoomOut': { + void viewer.zoomOut(); + break; + } case 'openExternal': { + void viewer.openExternal(); + break; + } case 'toggleFullWindow': { + void viewer.toggleFullWindow(); + break; + } default: { + break; + } + } + } +} + + +interface EjsData { + overwriteStyles: boolean; + previewPlotLayout: PreviewPlotLayout; + activePlot?: HttpgdPlotId; + plots: HttpgdPlot[]; + largePlot: HttpgdPlot; + host: string; + asLocalPath: (relPath: string) => string; + asWebViewPath: (localPath: string) => string; + makeCommandUri: (command: string, ...args: any[]) => string; + overwriteCssPath: string; + + // only used to render an individual smallPlot div: + plot?: HttpgdPlot; +} + +interface ShowOptions { + viewColumn: vscode.ViewColumn, + preserveFocus?: boolean +} + +export class HttpgdViewer implements IHttpgdViewer { + + readonly parent: HttpgdManager; + + readonly host: string; + readonly token?: string; + + // Actual webview where the plot viewer is shown + // Will have to be created anew, if the user closes it and the plot changes + webviewPanel?: vscode.WebviewPanel; + + // Api that provides plot contents etc. + readonly api: Httpgd; + + // active plots + plots: HttpgdPlot[] = []; + + // Id of the currently viewed plot + activePlot?: HttpgdPlotId; + + // Ids of plots that are not shown, but not closed inside httpgd + hiddenPlots: HttpgdPlotId[] = []; + + readonly defaultStripStyles: boolean = true; + stripStyles: boolean; + + readonly defaultPreviewPlotLayout: PreviewPlotLayout = 'multirow'; + previewPlotLayout: PreviewPlotLayout; + + readonly defaultFullWindow: boolean = false; + fullWindow: boolean; + + // Custom file to be used instead of `styleOverwrites.css` + customOverwriteCssPath?: string; + + // Size of the view area: + viewHeight: number = 600; + viewWidth: number = 800; + + // Size of the shown plot (as computed): + plotHeight: number = 600; + plotWidth: number = 800; + + readonly zoom0: number = 1; + zoom: number = this.zoom0; + + protected resizeTimeout?: NodeJS.Timeout; + readonly resizeTimeoutLength: number = 1300; + + protected refreshTimeout?: NodeJS.Timeout; + readonly refreshTimeoutLength: number = 10; + + private lastExportUri?: vscode.Uri; + + readonly htmlTemplate: string; + readonly smallPlotTemplate: string; + readonly htmlRoot: string; + + readonly showOptions: ShowOptions; + readonly webviewOptions: vscode.WebviewPanelOptions & vscode.WebviewOptions; + + // Computed properties: + + // Get/set active plot by index instead of id: + protected get activeIndex(): number { + if(!this.activePlot){ + return -1; + } + return this.getIndex(this.activePlot); + } + protected set activeIndex(ind: number) { + if (this.plots.length === 0) { + this.activePlot = undefined; + } else { + ind = Math.max(ind, 0); + ind = Math.min(ind, this.plots.length - 1); + this.activePlot = this.plots[ind].id; + } + } + + // constructor called by the session watcher if a corresponding function was called in R + // creates a new api instance itself + constructor(host: string, options: HttpgdViewerOptions) { + this.host = host; + this.token = options.token; + this.parent = options.parent; + + this.api = new Httpgd(this.host, this.token, true); + this.api.onPlotsChanged((newState) => { + void this.refreshPlotsDelayed(newState.plots); + }); + this.api.onConnectionChanged(() => { + // todo + }); + this.api.onDeviceActiveChanged(() => { + // todo + }); + const conf = config(); + this.customOverwriteCssPath = conf.get('plot.customStyleOverwrites', ''); + const localResourceRoots = ( + this.customOverwriteCssPath ? + [extensionContext.extensionUri, vscode.Uri.file(path.dirname(this.customOverwriteCssPath))] : + undefined + ); + this.htmlRoot = options.htmlRoot; + this.htmlTemplate = fs.readFileSync(path.join(this.htmlRoot, 'index.ejs'), 'utf-8'); + this.smallPlotTemplate = fs.readFileSync(path.join(this.htmlRoot, 'smallPlot.ejs'), 'utf-8'); + this.showOptions = { + viewColumn: options.viewColumn ?? asViewColumn(conf.get('session.viewers.viewColumn.plot'), vscode.ViewColumn.Two), + preserveFocus: !!options.preserveFocus + }; + this.webviewOptions = { + enableCommandUris: true, + enableScripts: true, + retainContextWhenHidden: true, + localResourceRoots: localResourceRoots + }; + this.defaultStripStyles = options.stripStyles ?? this.defaultStripStyles; + this.stripStyles = this.defaultStripStyles; + this.defaultPreviewPlotLayout = options.previewPlotLayout ?? this.defaultPreviewPlotLayout; + this.previewPlotLayout = this.defaultPreviewPlotLayout; + this.defaultFullWindow = options.fullWindow ?? this.defaultFullWindow; + this.fullWindow = this.defaultFullWindow; + this.resizeTimeoutLength = options.refreshTimeoutLength ?? this.resizeTimeoutLength; + this.refreshTimeoutLength = options.refreshTimeoutLength ?? this.refreshTimeoutLength; + void this.api.connect(); + //void this.checkState(); + } + + + // Methods to interact with the webview + // Can e.g. be called by vscode commands + menu items: + + // Called to create a new webview if the user closed the old one: + public show(preserveFocus?: boolean): void { + preserveFocus ??= this.showOptions.preserveFocus; + if (!this.webviewPanel) { + const showOptions = { + ...this.showOptions, + preserveFocus: preserveFocus + }; + this.webviewPanel = this.makeNewWebview(showOptions); + this.refreshHtml(); + } else { + this.webviewPanel.reveal(undefined, preserveFocus); + } + this.parent.registerActiveViewer(this); + } + + public openExternal(): void { + let urlString = `http://${this.host}/live`; + if (this.token) { + urlString += `?token=${this.token}`; + } + const uri = vscode.Uri.parse(urlString); + void vscode.env.openExternal(uri); + } + + // focus a specific plot id + public async focusPlot(id?: HttpgdPlotId): Promise { + this.activePlot = id || this.activePlot; + const plt = this.plots[this.activeIndex]; + if (plt.height !== this.viewHeight || plt.width !== this.viewHeight || plt.zoom !== this.zoom) { + await this.refreshPlots(this.api.getPlots()); + } else { + this._focusPlot(); + } + } + protected _focusPlot(plotId?: HttpgdPlotId): void { + plotId ??= this.activePlot; + if(!plotId){ + return; + } + const msg: FocusPlotMessage = { + message: 'focusPlot', + plotId: plotId + }; + this.postWebviewMessage(msg); + void this.setContextValues(); + } + + // navigate through plots (supply `true` to go to end/beginning of list) + public async nextPlot(last?: boolean): Promise { + this.activeIndex = last ? this.plots.length - 1 : this.activeIndex + 1; + await this.focusPlot(); + } + public async prevPlot(first?: boolean): Promise { + this.activeIndex = first ? 0 : this.activeIndex - 1; + await this.focusPlot(); + } + + // restore closed plots, reset zoom, redraw html + public resetPlots(): void { + this.hiddenPlots = []; + this.zoom = this.zoom0; + void this.refreshPlots(this.api.getPlots(), true, true); + } + + public hidePlot(id?: HttpgdPlotId): void { + id ??= this.activePlot; + if (!id) { return; } + const tmpIndex = this.activeIndex; + this.hiddenPlots.push(id); + this.plots = this.plots.filter((plt) => !this.hiddenPlots.includes(plt.id)); + if (id === this.activePlot) { + this.activeIndex = tmpIndex; + this._focusPlot(); + } + this._hidePlot(id); + } + protected _hidePlot(id: HttpgdPlotId): void { + const msg: HidePlotMessage = { + message: 'hidePlot', + plotId: id + }; + this.postWebviewMessage(msg); + } + + public async closePlot(id?: HttpgdPlotId): Promise { + id ??= this.activePlot; + if (id) { + this.hidePlot(id); + await this.api.removePlot({ id: id }); + } + } + + public toggleStyle(force?: boolean): void { + this.stripStyles = force ?? !this.stripStyles; + const msg: ToggleStyleMessage = { + message: 'toggleStyle', + useOverwrites: this.stripStyles + }; + this.postWebviewMessage(msg); + } + + public toggleFullWindow(force?: boolean): void { + this.fullWindow = force ?? !this.fullWindow; + const msg: ToggleFullWindowMessage = { + message: 'toggleFullWindow', + useFullWindow: this.fullWindow + }; + this.postWebviewMessage(msg); + } + + public togglePreviewPlots(force?: PreviewPlotLayout): void { + if (force) { + this.previewPlotLayout = force; + } else if (this.previewPlotLayout === 'multirow') { + this.previewPlotLayout = 'scroll'; + } else if (this.previewPlotLayout === 'scroll') { + this.previewPlotLayout = 'hidden'; + } else if (this.previewPlotLayout === 'hidden') { + this.previewPlotLayout = 'multirow'; + } + const msg: PreviewPlotLayoutMessage = { + message: 'togglePreviewPlotLayout', + style: this.previewPlotLayout + }; + this.postWebviewMessage(msg); + } + + public zoomOut(): void { + if (this.zoom > 0) { + this.zoom -= 0.1; + void this.resizePlot(); + } + } + + public zoomIn(): void { + this.zoom += 0.1; + void this.resizePlot(); + } + + + public async setContextValues(mightBeInBackground: boolean = false): Promise { + if (this.webviewPanel?.active) { + this.parent.registerActiveViewer(this); + await setContext('r.plot.active', true); + await setContext('r.plot.canGoBack', this.activeIndex > 0); + await setContext('r.plot.canGoForward', this.activeIndex < this.plots.length - 1); + } else if (!mightBeInBackground) { + await setContext('r.plot.active', false); + } + } + + public getPanelPath(): string | undefined { + if (!this.webviewPanel) { + return undefined; + } + const dummyUri = this.webviewPanel.webview.asWebviewUri(vscode.Uri.file('')); + const m = /^[^.]*/.exec(dummyUri.authority); + const webviewId = m?.[0] || ''; + return `webview-panel/webview-${webviewId}`; + } + + protected getIndex(id: HttpgdPlotId): number { + return this.plots.findIndex((plt: HttpgdPlot) => plt.id === id); + } + + protected handleResize(height: number, width: number, userTriggered: boolean = false): void { + this.viewHeight = height; + this.viewWidth = width; + if (userTriggered || this.resizeTimeoutLength === 0) { + if(this.resizeTimeout){ + clearTimeout(this.resizeTimeout); + } + this.resizeTimeout = undefined; + void this.resizePlot(); + } else if (!this.resizeTimeout) { + this.resizeTimeout = setTimeout(() => { + void this.resizePlot().then(() => + this.resizeTimeout = undefined + ); + }, this.resizeTimeoutLength); + } + } + + protected async resizePlot(id?: HttpgdPlotId): Promise { + id ??= this.activePlot; + if (!id) { return; } + const plt = await this.getPlotContent(id, this.viewWidth, this.viewHeight, this.zoom); + this.plotWidth = plt.width; + this.plotHeight = plt.height; + this.updatePlot(plt); + } + + protected async refreshPlotsDelayed(plotsIdResponse: HttpgdIdResponse[], redraw: boolean = false, force: boolean = false): Promise { + if(this.refreshTimeoutLength === 0){ + await this.refreshPlots(plotsIdResponse, redraw, force); + } else{ + clearTimeout(this.refreshTimeout); + this.refreshTimeout = setTimeout(() => { + void this.refreshPlots(plotsIdResponse, redraw, force).then(() => + this.refreshTimeout = undefined + ); + }, this.refreshTimeoutLength); + } + } + + protected async refreshPlots(plotsIdResponse: HttpgdIdResponse[], redraw: boolean = false, force: boolean = false): Promise { + const nPlots = this.plots.length; + let plotIds = plotsIdResponse.map((x) => x.id); + plotIds = plotIds.filter((id) => !this.hiddenPlots.includes(id)); + const newPlotPromises = plotIds.map(async (id) => { + const plot = this.plots.find((plt) => plt.id === id); + if (force || !plot || id === this.activePlot) { + return await this.getPlotContent(id, this.viewWidth, this.viewHeight, this.zoom); + } else { + return plot; + } + }); + const newPlots = await Promise.all(newPlotPromises); + const oldPlotIds = this.plots.map(plt => plt.id); + this.plots = newPlots; + if (this.plots.length !== nPlots) { + this.activePlot = this.plots[this.plots.length - 1]?.id; + } + if (redraw || !this.webviewPanel) { + this.refreshHtml(); + } else { + for (const plt of this.plots) { + if (oldPlotIds.includes(plt.id)) { + this.updatePlot(plt); + } else { + this.addPlot(plt); + } + } + this._focusPlot(); + } + } + + protected updatePlot(plt: HttpgdPlot): void { + const msg: UpdatePlotMessage = { + message: 'updatePlot', + plotId: plt.id, + svg: plt.data + }; + this.postWebviewMessage(msg); + } + + protected addPlot(plt: HttpgdPlot): void { + const ejsData = this.makeEjsData(); + ejsData.plot = plt; + const html = ejs.render(this.smallPlotTemplate, ejsData); + const msg: AddPlotMessage = { + message: 'addPlot', + html: html + }; + this.postWebviewMessage(msg); + void this.focusPlot(plt.id); + void this.setContextValues(); + } + + // get content of a single plot + protected async getPlotContent(id: HttpgdPlotId, width: number, height: number, zoom: number): Promise> { + + const args = { + id: id, + height: height, + width: width, + zoom: zoom, + renderer: 'svgp' + }; + + const plotContent = await this.api.getPlot(args); + const svg = await plotContent?.text() || ''; + + const plt: HttpgdPlot = { + id: id, + data: svg, + height: height, + width: width, + zoom: zoom, + }; + + this.viewHeight ??= plt.height; + this.viewWidth ??= plt.width; + return plt; + } + + + // functions for initial or re-drawing of html: + + protected refreshHtml(): void { + this.webviewPanel ??= this.makeNewWebview(); + this.webviewPanel.webview.html = ''; + this.webviewPanel.webview.html = this.makeHtml(); + // make sure that fullWindow is set correctly: + this.toggleFullWindow(this.fullWindow); + void this.setContextValues(true); + } + + protected makeHtml(): string { + const ejsData = this.makeEjsData(); + const html = ejs.render(this.htmlTemplate, ejsData); + return html; + } + + protected makeEjsData(): EjsData { + const asLocalPath = (relPath: string) => { + if (!this.webviewPanel) { + return relPath; + } + const localUri = vscode.Uri.file(path.join(this.htmlRoot, relPath)); + return localUri.fsPath; + }; + const asWebViewPath = (localPath: string) => { + if (!this.webviewPanel) { + return localPath; + } + const localUri = vscode.Uri.file(path.join(this.htmlRoot, localPath)); + const webViewUri = this.webviewPanel.webview.asWebviewUri(localUri); + return webViewUri.toString(); + }; + let overwriteCssPath = ''; + if (this.customOverwriteCssPath) { + const uri = vscode.Uri.file(this.customOverwriteCssPath); + overwriteCssPath = this.webviewPanel?.webview.asWebviewUri(uri).toString() || ''; + } else { + overwriteCssPath = asWebViewPath('styleOverwrites.css'); + } + const ejsData: EjsData = { + overwriteStyles: this.stripStyles, + previewPlotLayout: this.previewPlotLayout, + plots: this.plots, + largePlot: this.plots[this.activeIndex], + activePlot: this.activePlot, + host: this.host, + asLocalPath: asLocalPath, + asWebViewPath: asWebViewPath, + makeCommandUri: makeWebviewCommandUriString, + overwriteCssPath: overwriteCssPath + }; + return ejsData; + } + + protected makeNewWebview(showOptions?: ShowOptions): vscode.WebviewPanel { + const webviewPanel = vscode.window.createWebviewPanel( + 'RPlot', + 'R Plot', + showOptions || this.showOptions, + this.webviewOptions + ); + webviewPanel.iconPath = new UriIcon('graph'); + webviewPanel.onDidDispose(() => this.webviewPanel = undefined); + webviewPanel.onDidChangeViewState(() => { + void this.setContextValues(); + }); + webviewPanel.webview.onDidReceiveMessage((e: OutMessage) => { + this.handleWebviewMessage(e); + }); + return webviewPanel; + } + + protected handleWebviewMessage(msg: OutMessage): void { + if (msg.message === 'log') { + console.log(msg.body); + } else if (msg.message === 'resize') { + const height = msg.height; + const width = msg.width; + const userTriggered = msg.userTriggered; + void this.handleResize(height, width, userTriggered); + } + } + + protected postWebviewMessage(msg: InMessage): void { + void this.webviewPanel?.webview.postMessage(msg); + } + + + // export plot + // if no format supplied, show a quickpick menu etc. + // if no filename supplied, show selector window + public async exportPlot(id?: HttpgdPlotId, rendererId?: HttpgdRendererId, outFile?: string): Promise { + // make sure id is valid or return: + id ||= this.activePlot || this.plots[this.plots.length - 1]?.id; + const plot = this.plots.find((plt) => plt.id === id); + if (!plot) { + void vscode.window.showWarningMessage('No plot available for export.'); + return; + } + // make sure format is valid or return: + if (!rendererId) { + const renderers = this.api.getRenderers(); + const qpItems = renderers.map(renderer => ({ + label: renderer.name, + detail: renderer.descr, + id: renderer.id + })); + const options: vscode.QuickPickOptions = { + placeHolder: 'Please choose a file format' + }; + // format = await vscode.window.showQuickPick(formats, options); + const qpPick = await vscode.window.showQuickPick(qpItems, options); + rendererId = qpPick?.id; + if(!rendererId){ + return; + } + } + // make sure outFile is valid or return: + if (!outFile) { + const options: vscode.SaveDialogOptions = {}; + + // Suggest a file extension: + const renderer = this.api.getRenderers().find(r => r.id === rendererId); + const ext = renderer?.ext.replace(/^\./, ''); + + // try to set default URI: + if(this.lastExportUri){ + const noExtPath = this.lastExportUri.fsPath.replace(/\.[^.]*$/, ''); + const defaultPath = noExtPath + (ext ? `.${ext}` : ''); + options.defaultUri = vscode.Uri.file(defaultPath); + } else { + // construct default Uri + const defaultFolder = vscode.workspace.workspaceFolders?.[0]?.uri.fsPath; + if(defaultFolder){ + const defaultName = 'plot' + (ext ? `.${ext}` : ''); + options.defaultUri = vscode.Uri.file(path.join(defaultFolder, defaultName)); + } + } + // set file extension filter + if(ext && renderer?.name){ + options.filters = { + [renderer.name]: [ext], + ['All']: ['*'], + }; + } + + const outUri = await vscode.window.showSaveDialog(options); + if(outUri){ + this.lastExportUri = outUri; + outFile = outUri.fsPath; + } else { + return; + } + } + // get plot: + const plt = await this.api.getPlot({ + id: this.activePlot, + renderer: rendererId + }) as unknown as Response; // I am not sure why eslint thinks this is the + // browser Response object and not the node-fetch one. + // cross-fetch problem or config problem in vscode-r? + + const dest = fs.createWriteStream(outFile); + dest.on('error', (err) => void vscode.window.showErrorMessage( + `Export failed: ${err.message}` + )); + dest.on('close', () => void vscode.window.showInformationMessage( + `Export done: ${outFile || ''}` + )); + void plt.body.pipe(dest); + } + + // Dispose-function to clean up when vscode closes + // E.g. to close connections etc., notify R, ... + public dispose(): void { + this.api.disconnect(); + } +} + +// helper function to handle argument lists that might contain (useless) extra arguments +function findItemOfType(arr: any[], type: 'string'): string | undefined; +function findItemOfType(arr: any[], type: 'boolean'): boolean | undefined; +function findItemOfType(arr: any[], type: 'number'): number | undefined; +function findItemOfType(arr: any[], type: string): T { + const item = arr.find((elm) => typeof elm === type) as T; + return item; +} diff --git a/src/plotViewer/webviewMessages.d.ts b/src/plotViewer/webviewMessages.d.ts new file mode 100644 index 000000000..f503b1b8d --- /dev/null +++ b/src/plotViewer/webviewMessages.d.ts @@ -0,0 +1,71 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ + +// Make sure these files contain the same interfaces as ./html/XXX/webviewMessages.d.ts! + + +interface VsCode { + postMessage: (msg: OutMessage) => void; + setState: (state: string) => void; +} +declare function acquireVsCodeApi(): VsCode; + + + +export interface IMessage { + message: string; +} + +export interface ResizeMessage extends IMessage { + message: 'resize', + height: number, + width: number, + userTriggered: boolean +} +export interface LogMessage extends IMessage { + message: 'log', + body: any +} + +export type OutMessage = ResizeMessage | LogMessage; + + + +export interface UpdatePlotMessage extends IMessage { + message: 'updatePlot', + svg: string, + plotId: string +} + +export interface FocusPlotMessage extends IMessage { + message: 'focusPlot', + plotId: string +} + +export interface ToggleStyleMessage extends IMessage { + message: 'toggleStyle', + useOverwrites: boolean +} + +export interface ToggleFullWindowMessage extends IMessage { + message: 'toggleFullWindow', + useFullWindow: boolean +} + + +export type PreviewPlotLayout = 'multirow' | 'scroll' | 'hidden'; +export interface PreviewPlotLayoutMessage extends IMessage { + message: 'togglePreviewPlotLayout', + style: PreviewPlotLayout +} + +export interface HidePlotMessage extends IMessage { + message: 'hidePlot', + plotId: string +} + +export interface AddPlotMessage extends IMessage { + message: 'addPlot', + html: string +} + +export type InMessage = UpdatePlotMessage | FocusPlotMessage | ToggleStyleMessage | HidePlotMessage | AddPlotMessage | PreviewPlotLayoutMessage | ToggleFullWindowMessage; \ No newline at end of file diff --git a/src/preview.ts b/src/preview.ts index 4646e5f20..6df57035d 100644 --- a/src/preview.ts +++ b/src/preview.ts @@ -1,131 +1,138 @@ -"use strict"; +'use strict'; -import fs = require("fs-extra"); -import { commands, extensions, window, workspace } from "vscode"; -import { chooseTerminalAndSendText } from "./rTerminal"; -import { getSelection } from "./selection"; -import { checkForSpecialCharacters, checkIfFileExists, delay } from "./util"; +import { removeSync, statSync } from 'fs-extra'; +import { commands, extensions, window, workspace } from 'vscode'; -export async function previewEnvironment() { - if (!checkcsv()) { - return; +import { runTextInTerm } from './rTerminal'; +import { getWordOrSelection } from './selection'; +import { config, checkForSpecialCharacters, checkIfFileExists, delay, createTempDir, getCurrentWorkspaceFolder } from './util'; + +export async function previewEnvironment(): Promise { + if (config().get('sessionWatcher')) { + await runTextInTerm('View(globalenv())'); + } else { + if (!checkcsv()) { + return; + } + const currentWorkspaceFolder = getCurrentWorkspaceFolder()?.uri.fsPath; + if (!currentWorkspaceFolder) { + return; + } + const tmpDir = createTempDir(currentWorkspaceFolder, true); + const pathToTmpCsv = `${tmpDir}/environment.csv`; + const envName = 'name=ls()'; + const envClass = 'class=sapply(ls(), function(x) {class(get(x, envir = parent.env(environment())))[1]})'; + const envOut = 'out=sapply(ls(), function(x) {capture.output(str(get(x, envir = parent.env(environment()))), silent = T)[1]})'; + const rWriteCsvCommand = 'write.csv(data.frame(' + + `${envName},` + + `${envClass},` + + `${envOut}), '` + + `${pathToTmpCsv}', row.names=FALSE, quote = TRUE)`; + await runTextInTerm(rWriteCsvCommand); + await openTmpCSV(pathToTmpCsv, tmpDir); } - const tmpDir = makeTmpDir(); - const pathToTmpCsv = tmpDir + "/environment.csv"; - const envName = "name=ls()"; - const envClass = "class=sapply(ls(), function(x) {class(get(x, envir = parent.env(environment())))[1]})"; - const envOut = "out=sapply(ls(), function(x) {capture.output(str(get(x, envir = parent.env(environment()))), silent = T)[1]})"; - const rWriteCsvCommand = "write.csv(data.frame(" - + envName + "," - + envClass + "," - + envOut + "), '" - + pathToTmpCsv + "', row.names=FALSE, quote = TRUE)"; - chooseTerminalAndSendText(rWriteCsvCommand); - await openTmpCSV(pathToTmpCsv, tmpDir); } -export async function previewDataframe() { - if (!checkcsv()) { - return; - } +export async function previewDataframe(): Promise { + if (config().get('sessionWatcher')) { + const symbol = getWordOrSelection(); + await runTextInTerm(`View(${symbol || 'undefined'})`); + } else { + if (!checkcsv()) { + return; + } + const currentWorkspaceFolder = getCurrentWorkspaceFolder()?.uri.fsPath; + if (!currentWorkspaceFolder) { + return; + } - const selectedTextArray = getSelection().selectedTextArray; - const dataframeName = selectedTextArray[0]; + const dataframeName = getWordOrSelection(); + if (!dataframeName) { + return; + } - if (selectedTextArray.length !== 1 || !checkForSpecialCharacters(dataframeName)) { - window.showInformationMessage("This does not appear to be a dataframe."); - return false; - } + if (!checkForSpecialCharacters(dataframeName)) { + void window.showInformationMessage('This does not appear to be a dataframe.'); - const tmpDir = makeTmpDir(); + return false; + } + + const tmpDir = createTempDir(currentWorkspaceFolder, true); - // Create R write CSV command. Turn off row names and quotes, they mess with Excel Viewer. - const pathToTmpCsv = tmpDir + "/" + dataframeName + ".csv"; - const rWriteCsvCommand = "write.csv(" + dataframeName + ", '" - + pathToTmpCsv - + "', row.names = FALSE, quote = FALSE)"; - chooseTerminalAndSendText(rWriteCsvCommand); - await openTmpCSV(pathToTmpCsv, tmpDir); + // Create R write CSV command. Turn off row names and quotes, they mess with Excel Viewer. + const pathToTmpCsv = `${tmpDir}/${dataframeName}.csv`; + const rWriteCsvCommand = `write.csv(${dataframeName}, ` + + `'${pathToTmpCsv}', row.names = FALSE, quote = FALSE)`; + await runTextInTerm(rWriteCsvCommand); + await openTmpCSV(pathToTmpCsv, tmpDir); + } } -async function openTmpCSV(pathToTmpCsv: string, tmpDir: string) { +async function openTmpCSV(pathToTmpCsv: string, tmpDir: string): Promise { await delay(350); // Needed since file size has not yet changed if (!checkIfFileExists(pathToTmpCsv)) { - window.showErrorMessage("Dataframe failed to display."); - fs.removeSync(tmpDir); + void window.showErrorMessage('Dataframe failed to display.'); + removeSync(tmpDir); + return false; } // Async poll for R to complete writing CSV. const success = await waitForFileToFinish(pathToTmpCsv); if (!success) { - window.showWarningMessage("Visual Studio Code currently limits opening files to 20 MB."); - fs.removeSync(tmpDir); - return false; - } + void window.showWarningMessage('Visual Studio Code currently limits opening files to 20 MB.'); + removeSync(tmpDir); - if (process.platform === "win32") { - const winattr = require("winattr"); - winattr.setSync(tmpDir, {hidden: true}); + return false; } // Open CSV in Excel Viewer and clean up. - workspace.openTextDocument(pathToTmpCsv).then(async (file) => { - await commands.executeCommand("csv.preview", file.uri); - fs.removeSync(tmpDir); - }); + void workspace.openTextDocument(pathToTmpCsv).then( + async (file) => { + await commands.executeCommand('csv.preview', file.uri); + removeSync(tmpDir); + } + ); } -async function waitForFileToFinish(filePath) { +async function waitForFileToFinish(filePath: string): Promise { const fileBusy = true; let currentSize = 0; let previousSize = 1; while (fileBusy) { - const stats = fs.statSync(filePath); + const stats = statSync(filePath); currentSize = stats.size; // UPDATE: We are now limited to 20 mb by MODEL_TOKENIZATION_LIMIT - // https://github.com/Microsoft/vscode/blob/master/src/vs/editor/common/model/textModel.ts#L34 + // Https://github.com/Microsoft/vscode/blob/master/src/vs/editor/common/model/textModel.ts#L34 if (currentSize > 2 * 10000000) { // 20 MB return false; } if (currentSize === previousSize) { return true; - } else { - previousSize = currentSize; } + previousSize = currentSize; await delay(50); } } -function makeTmpDir() { - let tmpDir = workspace.workspaceFolders[0].uri.path; - if (process.platform === "win32") { - tmpDir = tmpDir.replace(/\\/g, "/"); - tmpDir += "/tmp"; - } else { - tmpDir += "/.tmp"; - } - if (!fs.existsSync(tmpDir)) { - fs.mkdirSync(tmpDir); - } - return tmpDir; -} - -function checkcsv() { - const iscsv = extensions.getExtension("GrapeCity.gc-excelviewer"); - if (iscsv && iscsv.isActive) { +function checkcsv(): boolean { + const iscsv = extensions.getExtension('GrapeCity.gc-excelviewer'); + if (iscsv !== undefined && iscsv.isActive) { return true; - } else { - window.showInformationMessage("This function need to install `GrapeCity.gc-excelviewer`, will you install?", - "Yes", "No").then((select) => { - if (select === "Yes") { - commands.executeCommand("workbench.extensions.installExtension", "GrapeCity.gc-excelviewer"); - } - }); - return false; } + void window.showInformationMessage( + 'This function need to install `GrapeCity.gc-excelviewer`, will you install?', + 'Yes', + 'No' + ).then((select) => { + if (select === 'Yes') { + void commands.executeCommand('workbench.extensions.installExtension', 'GrapeCity.gc-excelviewer'); + } + }); + + return false; } diff --git a/src/rGitignore.ts b/src/rGitignore.ts index 0669c79a0..93e72dfd7 100644 --- a/src/rGitignore.ts +++ b/src/rGitignore.ts @@ -1,38 +1,40 @@ -"use strict"; +/* eslint-disable @typescript-eslint/no-unsafe-argument */ +'use strict'; -import fs = require("fs-extra"); -import path = require("path"); -import { window, workspace } from "vscode"; -// From "https://github.com/github/gitignore/raw/master/R.gitignore" -const ignoreFiles = [".Rhistory", - ".Rapp.history", - ".RData", - "*-Ex.R", - "/*.tar.gz", - "/*.Rcheck/", - ".Rproj.user/", - "vignettes/*.html", - "vignettes/*.pdf", - ".httr-oauth", - "/*_cache/", - "/cache/", - "*.utf8.md", - "*.knit.md", - "rsconnect/"].join("\n"); +import { writeFile } from 'fs-extra'; +import { existsSync, readFileSync } from 'fs'; +import { join } from 'path'; +import { window } from 'vscode'; +import { extensionContext } from './extension'; +import { catchAsError, getCurrentWorkspaceFolder } from './util'; -export function createGitignore() { - if (!workspace.workspaceFolders[0].uri.path) { - window.showWarningMessage("Please open workspace to create .gitignore"); +export async function createGitignore(): Promise { + // .gitignore template from "https://github.com/github/gitignore/blob/main/R.gitignore" + const ignoreFileTemplate = extensionContext.asAbsolutePath('R/template/R.gitignore'); + const ignoreFileContent = readFileSync(ignoreFileTemplate); + + const currentWorkspaceFolder = getCurrentWorkspaceFolder()?.uri.fsPath; + if (currentWorkspaceFolder === undefined) { + void window.showWarningMessage('Please open a workspace folder to create .gitignore'); return; } - const ignorePath = path.join(workspace.workspaceFolders[0].uri.path, ".gitignore"); - fs.writeFile(ignorePath, ignoreFiles, (err) => { + const ignorePath = join(currentWorkspaceFolder, '.gitignore'); + if (existsSync(ignorePath)) { + const overwrite = await window.showWarningMessage( + '".gitignore" file already exists. Do you want to overwrite?', + 'Yes', 'No' + ); + if (overwrite === 'No') { + return; + } + } + writeFile(ignorePath, ignoreFileContent, (err) => { try { if (err) { - window.showErrorMessage(err.name); + void window.showErrorMessage(err.name); } } catch (e) { - window.showErrorMessage(e.message); + void window.showErrorMessage(catchAsError(e).message); } }); } diff --git a/src/rTerminal.ts b/src/rTerminal.ts index 062389dae..a71ee7e53 100644 --- a/src/rTerminal.ts +++ b/src/rTerminal.ts @@ -1,118 +1,323 @@ -"use strict"; - -import fs = require("fs-extra"); -import { isNull } from "util"; -import { commands, Terminal, window } from "vscode"; -import { getSelection } from "./selection"; -import { config, delay, getRpath } from "./util"; -export let rTerm: Terminal; - -export function createRTerm(preserveshow?: boolean): boolean { - const termName = "R Interactive"; - const termPath = getRpath(); - if (!termPath) { - return; - } - const termOpt = config.get("rterm.option") as string[]; - fs.pathExists(termPath, (err, exists) => { - if (exists) { - rTerm = window.createTerminal(termName, termPath, termOpt); - rTerm.show(preserveshow); - return true; - } else { - window.showErrorMessage("Cannot find R client. Please check R path in preferences and reload."); - return false; - } - }); +'use strict'; + +import * as path from 'path'; +import { isDeepStrictEqual } from 'util'; + +import * as vscode from 'vscode'; + +import { extensionContext, homeExtDir } from './extension'; +import * as util from './util'; +import * as selection from './selection'; +import { getSelection } from './selection'; +import { cleanupSession } from './session'; +import { config, delay, getRterm, getCurrentWorkspaceFolder } from './util'; +import { rGuestService, isGuestSession } from './liveShare'; +import * as fs from 'fs'; +export let rTerm: vscode.Terminal | undefined = undefined; + +export async function runSource(echo: boolean): Promise { + const wad = vscode.window.activeTextEditor?.document; + if (!wad) { + return; + } + const isSaved = await util.saveDocument(wad); + if (!isSaved) { + return; + } + let rPath: string = util.ToRStringLiteral(wad.fileName, '"'); + let encodingParam = util.config().get('source.encoding'); + if (encodingParam === undefined) { + return; + } + encodingParam = `encoding = "${encodingParam}"`; + const echoParam = util.config().get('source.echo'); + rPath = [rPath, encodingParam].join(', '); + if (echoParam) { + echo = true; + } + if (echo) { + rPath = [rPath, 'echo = TRUE'].join(', '); } + void runTextInTerm(`source(${rPath})`); +} + +export async function runSelection(): Promise { + await runSelectionInTerm(true); +} -export function deleteTerminal(term: Terminal) { - if (term === rTerm) { - rTerm = null; +export async function runSelectionRetainCursor(): Promise { + await runSelectionInTerm(false); +} + +export async function runSelectionOrWord(rFunctionName: string[]): Promise { + const text = selection.getWordOrSelection(); + if (!text) { + return; } + const wrappedText = selection.surroundSelection(text, rFunctionName); + await runTextInTerm(wrappedText); } -export async function chooseTerminal(active: boolean = false) { - if (active || config.get("alwaysUseActiveTerminal")) { - if (window.terminals.length < 1) { - window.showInformationMessage("There are no open terminals."); - return null; - } else { - return window.activeTerminal; - } +export async function runCommandWithSelectionOrWord(rCommand: string): Promise { + const text = selection.getWordOrSelection(); + if (!text) { + return; } + const call = rCommand.replace(/\$\$/g, text); + await runTextInTerm(call); +} - if (window.terminals.length > 0) { - const RTermNameOpinions = ["R", "R Interactive"]; - if (window.activeTerminal) { - const activeTerminalName = window.activeTerminal.name; - if (RTermNameOpinions.includes(activeTerminalName)) { - return window.activeTerminal; - } - } else { - // Creating a terminal when there aren't any already - // does not seem to set activeTerminal - if (window.terminals.length === 1) { - const activeTerminalName = window.terminals[0].name; - if (RTermNameOpinions.includes(activeTerminalName)) { - return window.terminals[0]; +export async function runCommandWithEditorPath(rCommand: string): Promise { + const textEditor = vscode.window.activeTextEditor; + if (!textEditor) { + return; + } + const wad: vscode.TextDocument = textEditor.document; + const isSaved = await util.saveDocument(wad); + if (isSaved) { + const rPath = util.ToRStringLiteral(wad.fileName, ''); + const call = rCommand.replace(/\$\$/g, rPath); + await runTextInTerm(call); + } +} + +export async function runCommand(rCommand: string): Promise { + await runTextInTerm(rCommand); +} + +export async function runFromBeginningToLine(): Promise { + const textEditor = vscode.window.activeTextEditor; + if (!textEditor) { + return; + } + const endLine = textEditor.selection.end.line; + const charactersOnLine = textEditor.document.lineAt(endLine).text.length; + const endPos = new vscode.Position(endLine, charactersOnLine); + const range = new vscode.Range(new vscode.Position(0, 0), endPos); + const text = textEditor.document.getText(range); + if (text === undefined) { + return; + } + await runTextInTerm(text); +} + +export async function runFromLineToEnd(): Promise { + const textEditor = vscode.window.activeTextEditor; + if (!textEditor) { + return; + } + const startLine = textEditor.selection.start.line; + const startPos = new vscode.Position(startLine, 0); + const endLine = textEditor.document.lineCount; + const range = new vscode.Range(startPos, new vscode.Position(endLine, 0)); + const text = textEditor.document.getText(range); + await runTextInTerm(text); +} + +export async function makeTerminalOptions(): Promise { + const workspaceFolderPath = getCurrentWorkspaceFolder()?.uri.fsPath; + const termPath = await getRterm(); + const shellArgs: string[] = config().get('rterm.option')?.map(util.substituteVariables) || []; + const termOptions: vscode.TerminalOptions = { + name: 'R Interactive', + shellPath: termPath, + shellArgs: shellArgs, + cwd: workspaceFolderPath, + }; + const newRprofile = extensionContext.asAbsolutePath(path.join('R', 'session', 'profile.R')); + const initR = extensionContext.asAbsolutePath(path.join('R', 'session','init.R')); + if (config().get('sessionWatcher')) { + termOptions.env = { + R_PROFILE_USER_OLD: process.env.R_PROFILE_USER, + R_PROFILE_USER: newRprofile, + VSCODE_INIT_R: initR, + VSCODE_WATCHER_DIR: homeExtDir() + }; + } + return termOptions; +} + +export async function createRTerm(preserveshow?: boolean): Promise { + const termOptions = await makeTerminalOptions(); + const termPath = termOptions.shellPath; + if(!termPath){ + void vscode.window.showErrorMessage('Could not find R path. Please check r.rterm and r.rpath setting.'); + return false; + } else if(!fs.existsSync(termPath)){ + void vscode.window.showErrorMessage(`Cannot find R client at ${termPath}. Please check r.rterm setting.`); + return false; + } + rTerm = vscode.window.createTerminal(termOptions); + rTerm.show(preserveshow); + return true; +} + +export async function restartRTerminal(): Promise{ + if (typeof rTerm !== 'undefined'){ + rTerm.dispose(); + deleteTerminal(rTerm); + await createRTerm(true); + } +} + +export function deleteTerminal(term: vscode.Terminal): void { + if (isDeepStrictEqual(term, rTerm)) { + rTerm = undefined; + if (config().get('sessionWatcher')) { + void term.processId.then((v) => { + if (v) { + void cleanupSession(v.toString()); } - } else { - // tslint:disable-next-line: max-line-length - window.showInformationMessage("Error identifying terminal! This shouldn't happen, so please file an issue at https://github.com/Ikuyadeu/vscode-R/issues"); - return null; - } + }); } } +} - if (!rTerm) { - const success = createRTerm(true); - await delay(200); // Let RTerm warm up - if (!success) { - return null; +export async function chooseTerminal(): Promise { + // VSCode Python's extension creates hidden terminal with string 'Deactivate' + // For now ignore terminals with this string + const ignoreTermIdentifier = 'Deactivate'; + + // Filter out terminals to be ignored + const visibleTerminals = vscode.window.terminals.filter(terminal => { + return !terminal.name.toLowerCase().includes(ignoreTermIdentifier); + }); + + if (config().get('alwaysUseActiveTerminal')) { + if (visibleTerminals.length < 1) { + void vscode.window.showInformationMessage('There are no open terminals.'); + return undefined; } + + return vscode.window.activeTerminal; + } + + let msg = '[chooseTerminal] '; + msg += `A. There are ${vscode.window.terminals.length} terminals: `; + for (let i = 0; i < vscode.window.terminals.length; i++){ + msg += `Terminal ${i}: ${vscode.window.terminals[i].name} `; + } + + const rTermNameOptions = ['R', 'R Interactive']; + + const validRTerminals = visibleTerminals.filter(terminal => { + return rTermNameOptions.includes(terminal.name); + }); + + if (validRTerminals.length > 0) { + // If there is an active terminal that is an R terminal, use it + if (vscode.window.activeTerminal && rTermNameOptions.includes(vscode.window.activeTerminal.name)) { + return vscode.window.activeTerminal; + } + // Otherwise, use last valid R terminal + const rTerminal = validRTerminals[validRTerminals.length - 1]; + rTerminal.show(true); + return rTerminal; + } else { + // If no valid R terminals are found, create a new one + console.info(msg); + await createRTerm(true); + await delay(200); // Let RTerm warm up + return rTerm; } - return rTerm; } -export async function runSelectionInTerm(term: Terminal, rFunctionName: string[]) { + +export async function runSelectionInTerm(moveCursor: boolean, useRepl = true): Promise { const selection = getSelection(); - if (selection.linesDownToMoveCursor > 0) { - commands.executeCommand("cursorMove", { to: "down", value: selection.linesDownToMoveCursor }); - commands.executeCommand("cursorMove", { to: "wrappedLineFirstNonWhitespaceCharacter" }); + if (!selection) { + return; + } + if (moveCursor && selection.linesDownToMoveCursor > 0) { + const textEditor = vscode.window.activeTextEditor; + if (!textEditor) { + return; + } + const lineCount = textEditor.document.lineCount; + if (selection.linesDownToMoveCursor + textEditor.selection.end.line === lineCount) { + const endPos = new vscode.Position(lineCount, textEditor.document.lineAt(lineCount - 1).text.length); + await textEditor.edit(e => e.insert(endPos, '\n')); + } + await vscode.commands.executeCommand('cursorMove', { to: 'down', value: selection.linesDownToMoveCursor }); + await vscode.commands.executeCommand('cursorMove', { to: 'wrappedLineFirstNonWhitespaceCharacter' }); } + if(useRepl && vscode.debug.activeDebugSession?.type === 'R-Debugger'){ + await sendRangeToRepl(selection.range); + } else{ + await runTextInTerm(selection.selectedText); + } +} - if (selection.selectedTextArray.length > 1 && config.get("bracketedPaste")) { - // Surround with ANSI control characters for bracketed paste mode - selection.selectedTextArray[0] = "\x1b[200~" + selection.selectedTextArray[0]; - selection.selectedTextArray[selection.selectedTextArray.length - 1] += "\x1b[201~"; +export async function runChunksInTerm(chunks: vscode.Range[]): Promise { + const textEditor = vscode.window.activeTextEditor; + if (!textEditor) { + return; } + const text = chunks + .map((chunk) => textEditor.document.getText(chunk).trim()) + .filter((chunk) => chunk.length > 0) + .join('\n'); + if (text.length > 0) { + return runTextInTerm(text); + } +} - for (let line of selection.selectedTextArray) { - await delay(8); // Increase delay if RTerm can't handle speed. +export async function runTextInTerm(text: string, execute: boolean = true): Promise { + if (isGuestSession) { + rGuestService?.requestRunTextInTerm(text); + } else { + const term = await chooseTerminal(); + if (term === undefined) { + return; + } + if (config().get('bracketedPaste')) { + // Surround with ANSI control characters for bracketed paste mode + text = `\x1b[200~${text}\x1b[201~`; + term.sendText(text, execute); + } else { + const rtermSendDelay: number = config().get('rtermSendDelay') || 8; + const split = text.split('\n'); + const last_split = split.length - 1; + for (const [count, line] of split.entries()) { + if (count > 0) { + await delay(rtermSendDelay); // Increase delay if RTerm can't handle speed. + } - if (rFunctionName && rFunctionName.length) { - let rFunctionCall = ""; - for (const feature of rFunctionName) { - rFunctionCall += feature + "("; + // Avoid sending newline on last line + if (count === last_split && !execute) { + term.sendText(line, false); + } else { + term.sendText(line); + } } - line = rFunctionCall + line.trim() + ")".repeat(rFunctionName.length); } - term.sendText(line); + setFocus(term); + // Scroll console to see latest output + await vscode.commands.executeCommand('workbench.action.terminal.scrollToBottom'); } - setFocus(term); } -export async function chooseTerminalAndSendText(text: string) { - const callableTerminal = await chooseTerminal(); - if (isNull(callableTerminal)) { - return; +function setFocus(term: vscode.Terminal) { + const focus: string = config().get('source.focus') || 'editor'; + if (focus !== 'none') { + term.show(focus !== 'terminal'); } - callableTerminal.sendText(text); - setFocus(callableTerminal); } -function setFocus(term: Terminal) { - const focus = config.get("source.focus") as string; - term.show(focus !== "terminal"); +export async function sendRangeToRepl(rng: vscode.Range): Promise { + const editor = vscode.window.activeTextEditor; + if (!editor) { + return; + } + const sel0 = editor.selections; + let sel1 = new vscode.Selection(rng.start, rng.end); + while(/^[\r\n]/.exec(editor.document.getText(sel1))){ + sel1 = new vscode.Selection(sel1.start.translate(1), sel1.end); + } + while(/\r?\n\r?\n$/.exec(editor.document.getText(sel1))){ + sel1 = new vscode.Selection(sel1.start, sel1.end.translate(-1)); + } + editor.selections = [sel1]; + await vscode.commands.executeCommand('editor.debug.action.selectionToRepl'); + editor.selections = sel0; } diff --git a/src/rmarkdown/chunks.ts b/src/rmarkdown/chunks.ts new file mode 100644 index 000000000..cb10f9787 --- /dev/null +++ b/src/rmarkdown/chunks.ts @@ -0,0 +1,499 @@ +import * as vscode from 'vscode'; +import { config } from '../util'; +import { runChunksInTerm } from '../rTerminal'; + +export function isRDocument(document: vscode.TextDocument) { + return (document.languageId === 'r'); +} + +function isRChunkLine(text: string) { + return (!!text.match(/^#+\s*%%/g)); +} + +function isChunkStartLine(text: string, isRDoc: boolean) { + if (isRDoc) { + return (isRChunkLine(text)); + } else { + return (!!text.match(/^\s*```+\s*\{\w+\s*.*$/g)); + } +} + +function isChunkEndLine(text: string, isRDoc: boolean) { + if (isRDoc) { + const isSectionHeader = text.match(/^#+\s*.*[-#+=*]{4,}/g); + return (isRChunkLine(text) || isSectionHeader); + } else { + return (!!text.match(/^\s*```+\s*$/g)); + } +} + +function getChunkLanguage(text: string, isRDoc: boolean = false) { + if (isRDoc) { + return 'r'; + } + return text.replace(/^\s*```+\s*\{(\w+)\s*.*\}\s*$/g, '$1').toLowerCase(); +} + +function getChunkOptions(text: string, isRDoc: boolean = false) { + if (isRDoc) { + return text.replace(/^#+\s*%%/g, ''); + } else { + return text.replace(/^\s*```+\s*\{\w+\s*,?\s*(.*)\s*\}\s*$/g, '$1'); + } +} + +function getChunkEval(chunkOptions: string) { + return (!chunkOptions.match(/eval\s*=\s*(F|FALSE)/g)); +} + +// This is for #| style chunk options +function isOptionComment(text: string) { + return (!!text.match(/^#+\|/g)); +} +export function shouldDisplayChunkOptions(document: vscode.TextDocument, position: vscode.Position) { + const line = document.lineAt(position).text; + const isRDoc = isRDocument(document); + const currentChunk = getCurrentChunk(getChunks(document), position.line); + const withinChunk = currentChunk && isWithinChunk(currentChunk, position.line); + if (!withinChunk) { + return false; + } + + const isRChunk = isRDoc ? + true : + getChunkLanguage(document.lineAt(currentChunk?.startLine).text, isRDoc) === 'r'; + + return isRChunk && (isChunkStartLine(line, isRDoc) || isOptionComment(line)); +} + +export interface RMarkdownChunk { + id: number; + startLine: number; + endLine: number; + language: string | undefined; + options: string | undefined; + eval: boolean | undefined; + chunkRange: vscode.Range; + codeRange: vscode.Range; +} + +// Scan document and return chunk info (e.g. ID, chunk range) from all chunks +export function getChunks(document: vscode.TextDocument): RMarkdownChunk[] { + const lines = document.getText().split(/\r?\n/); + const chunks: RMarkdownChunk[] = []; + + let line = 0; + let chunkId = 0; // One-based index + let chunkStartLine: number | undefined = undefined; + let chunkEndLine: number | undefined = undefined; + let codeEndLine: number | undefined = undefined; + let chunkLanguage: string | undefined = undefined; + let chunkOptions: string | undefined = undefined; + let chunkEval: boolean | undefined = undefined; + const isRDoc = isRDocument(document); + + while (line < lines.length) { + if (chunkStartLine === undefined) { + if (isChunkStartLine(lines[line], isRDoc)) { + chunkId++; + chunkStartLine = line; + chunkLanguage = getChunkLanguage(lines[line], isRDoc); + chunkOptions = getChunkOptions(lines[line], isRDoc); + chunkEval = getChunkEval(chunkOptions); + } + } else { + // Second condition is for the last chunk in an .R file + const isRDocAndFinalLine = (isRDoc && line === lines.length - 1); + if (isChunkEndLine(lines[line], isRDoc) || isRDocAndFinalLine) { + chunkEndLine = line; + codeEndLine = line - 1; + + // isChunkEndLine looks for `# %%` in `.R` files, so if found, then need to go back one line to mark end of code chunk. + if (isRDoc && !isRDocAndFinalLine) { + chunkEndLine = chunkEndLine - 1; + codeEndLine = chunkEndLine; + line = line - 1; + } + + const chunkRange = new vscode.Range( + new vscode.Position(chunkStartLine, 0), + new vscode.Position(line, lines[line].length) + ); + const codeRange = new vscode.Range( + new vscode.Position(chunkStartLine + 1, 0), + new vscode.Position(codeEndLine, lines[codeEndLine].length) + ); + + chunks.push({ + id: chunkId, // One-based index + startLine: chunkStartLine, + endLine: chunkEndLine, + language: chunkLanguage, + options: chunkOptions, + eval: chunkEval, + chunkRange: chunkRange, + codeRange: codeRange + }); + + chunkStartLine = undefined; + } + } + line++; + } + return chunks; +} + +export function getCurrentChunk(chunks: RMarkdownChunk[], line: number): RMarkdownChunk | undefined { + const textEditor = vscode.window.activeTextEditor; + if (!textEditor) { + void vscode.window.showWarningMessage('No text editor active.'); + return; + } + + // Case: If `chunks` is empty, return undefined + if (chunks.length === 0) { + return undefined; + } + + // Case: Cursor is above first chunk, use first chunk + if (line < chunks[0].startLine) { + return chunks[0]; + } + // Case: Cursor is below last chunk, return last chunk + if (line > chunks[chunks.length - 1].endLine) { + return chunks[chunks.length - 1]; + } + // chunks.filter(i => line >= i.startLine)[0]; + for (const chunk of chunks) { + // Case: Cursor is within chunk, use current chunk + // Case: Cursor is between, use next chunk below cursor + if (chunk.endLine >= line) { + return chunk; + } + } +} + +function getPreviousChunk(chunks: RMarkdownChunk[], line: number): RMarkdownChunk | undefined { + const currentChunk = getCurrentChunk(chunks, line); + if (!currentChunk) { + return undefined; + } + if (currentChunk.id !== 1) { + // When cursor is below the last 'chunk end line', the definition of the previous chunk is the last chunk + const previousChunkId = currentChunk.endLine < line ? currentChunk.id : currentChunk.id - 1; + const previousChunk = chunks.find(i => i.id === previousChunkId); + return previousChunk; + } else { + return (currentChunk); + } +} + +function getNextChunk(chunks: RMarkdownChunk[], line: number): RMarkdownChunk | undefined { + const currentChunk = getCurrentChunk(chunks, line); + if (!currentChunk) { + return undefined; + } + if (currentChunk.id !== chunks.length) { + // When cursor is above the first 'chunk start line', the definition of the next chunk is the first chunk + const nextChunkId = line < currentChunk.startLine ? currentChunk.id : currentChunk.id + 1; + const nextChunk = chunks.find(i => i.id === nextChunkId); + return nextChunk; + } else { + return currentChunk; + } + +} + +// Helpers +function _getChunks(): RMarkdownChunk[] { + const textEditor = vscode.window.activeTextEditor; + if (!textEditor) { + return []; + } + return getChunks(textEditor.document); +} +function _getStartLine(): number { + const textEditor = vscode.window.activeTextEditor; + if (!textEditor) { + return 0; + } + return textEditor.selection.start.line; +} +export function isWithinChunk(chunk: RMarkdownChunk, line: number = _getStartLine()): boolean { + return (line >= chunk.startLine && line <= chunk.endLine); +} + +export async function runCurrentChunk(chunks: RMarkdownChunk[] = _getChunks(), + line: number = _getStartLine()): Promise { + const currentChunk = getCurrentChunk(chunks, line); + if (currentChunk) { + await runChunksInTerm([currentChunk.codeRange]); + } +} + +export async function runCurrentChunkAndMove(chunks: RMarkdownChunk[] = _getChunks(), + line: number = _getStartLine()): Promise { + const currentChunk = getCurrentChunk(chunks, line); + if (currentChunk) { + await runChunksInTerm([currentChunk.codeRange]); + } + const nextChunk = getNextChunk(chunks, line); + if (nextChunk) { + void goToChunk(nextChunk); + } +} + +export async function runPreviousChunk(chunks: RMarkdownChunk[] = _getChunks(), + line: number = _getStartLine()): Promise { + const currentChunk = getCurrentChunk(chunks, line); + const previousChunk = getPreviousChunk(chunks, line); + + // Case: cursor is below the last chunk, run last chunk + if (currentChunk && line > currentChunk.endLine) { + await(runChunksInTerm([currentChunk.codeRange])); + // Case: currentChunk is not the first chunk, so run previousChunk + } else if (previousChunk && previousChunk !== currentChunk) { + await runChunksInTerm([previousChunk.codeRange]); + } + +} + +export async function runNextChunk(chunks: RMarkdownChunk[] = _getChunks(), + line: number = _getStartLine()): Promise { + const currentChunk = getCurrentChunk(chunks, line); + const nextChunk = getNextChunk(chunks, line); + + // Case: currentChunk is not the last chunk, so run nextChunk + if (nextChunk && nextChunk !== currentChunk) { + await runChunksInTerm([nextChunk.codeRange]); + } +} + +export async function runAboveChunks(chunks: RMarkdownChunk[] = _getChunks(), + line: number = _getStartLine()): Promise { + const currentChunk = getCurrentChunk(chunks, line); + const previousChunk = getPreviousChunk(chunks, line); + if (!currentChunk || !previousChunk) { + return; + } + const firstChunkId = 1; + const previousChunkId = previousChunk.id; + + const codeRanges: vscode.Range[] = []; + + // Only do something if current chunk is not the first chunk + if (currentChunk.id > 1) { + for (let i = firstChunkId; i <= previousChunkId; i++) { + const chunk = chunks.find(e => e.id === i); + if (chunk?.eval) { + codeRanges.push(chunk.codeRange); + } + } + await runChunksInTerm(codeRanges); + } +} + +export async function runBelowChunks(chunks: RMarkdownChunk[] = _getChunks(), + line: number = _getStartLine()): Promise { + + const currentChunk = getCurrentChunk(chunks, line); + const nextChunk = getNextChunk(chunks, line); + if (!currentChunk || !nextChunk) { + return; + } + const nextChunkId = nextChunk.id; + const lastChunkId = chunks.length; + + const codeRanges: vscode.Range[] = []; + + // Only do something if current chunk is not the last chunk + if (currentChunk.id < lastChunkId) { + for (let i = nextChunkId; i <= lastChunkId; i++) { + const chunk = chunks.find(e => e.id === i); + if (chunk?.eval) { + codeRanges.push(chunk.codeRange); + } + } + await runChunksInTerm(codeRanges); + } +} + +export async function runCurrentAndBelowChunks(chunks: RMarkdownChunk[] = _getChunks(), + line: number = _getStartLine()): Promise { + const currentChunk = getCurrentChunk(chunks, line); + if (!currentChunk) { + return; + } + const currentChunkId = currentChunk.id; + const lastChunkId = chunks.length; + + const codeRanges: vscode.Range[] = []; + + for (let i = currentChunkId; i <= lastChunkId; i++) { + const chunk = chunks.find(e => e.id === i); + if (chunk) { + codeRanges.push(chunk.codeRange); + } + } + await runChunksInTerm(codeRanges); +} + +export async function runAllChunks(chunks: RMarkdownChunk[] = _getChunks()): Promise { + + const firstChunkId = 1; + const lastChunkId = chunks.length; + + const codeRanges: vscode.Range[] = []; + + for (let i = firstChunkId; i <= lastChunkId; i++) { + const chunk = chunks.find(e => e.id === i); + if (chunk?.eval) { + codeRanges.push(chunk.codeRange); + } + } + await runChunksInTerm(codeRanges); +} + +async function goToChunk(chunk: RMarkdownChunk) { + // Move cursor 1 line below 'chunk start line' + const line = chunk.startLine + 1; + const editor = vscode.window.activeTextEditor; + if (!editor) { + return; + } + editor.selection = new vscode.Selection(line, 0, line, 0); + await vscode.commands.executeCommand('revealLine', { lineNumber: line, at: 'center' }); +} + +export function goToPreviousChunk(chunks: RMarkdownChunk[] = _getChunks(), + line: number = _getStartLine()): void { + const previousChunk = getPreviousChunk(chunks, line); + if (previousChunk) { + void goToChunk(previousChunk); + } +} + +export function goToNextChunk(chunks: RMarkdownChunk[] = _getChunks(), + line: number = _getStartLine()): void { + const nextChunk = getNextChunk(chunks, line); + if (nextChunk) { + void goToChunk(nextChunk); + } +} + +export function selectCurrentChunk(chunks: RMarkdownChunk[] = _getChunks(), + line: number = _getStartLine()): void { + const editor = vscode.window.activeTextEditor; + const currentChunk = getCurrentChunk(chunks, line); + if (!editor || !currentChunk || !isWithinChunk(currentChunk, line)) { + return; + } + const lines = editor.document.getText().split(/\r?\n/); + + editor.selection = new vscode.Selection( + currentChunk.startLine, 0, + currentChunk.endLine, lines[currentChunk.endLine].length + ); +} + +export function getCodeLenses(chunks: RMarkdownChunk[], token: vscode.CancellationToken): vscode.CodeLens[] { + + const enabledCodeLens = config().get('rmarkdown.enableCodeLens'); + if (enabledCodeLens === false) { + return []; + } + + // Iterate through all code chunks for getting chunk information for both CodeLens and chunk background color (set by `editor.setDecorations`) + let codeLenses: vscode.CodeLens[] = []; + for (let i = 1; i <= chunks.length; i++) { + const chunk = chunks.find(e => e.id === i); + if (!chunk) { + continue; + } + const chunkRange = chunk.chunkRange; + const line = chunk.startLine; + + // Enable/disable only CodeLens, without affecting chunk background color. + if (chunk.language === 'r') { + if (token.isCancellationRequested) { + break; + } + codeLenses.push( + new vscode.CodeLens(chunkRange, { + title: 'Run Chunk', + tooltip: 'Run current chunk', + command: 'r.runCurrentChunk', + arguments: [chunks, line] + }), + new vscode.CodeLens(chunkRange, { + title: 'Run Above', + tooltip: 'Run all chunks above', + command: 'r.runAboveChunks', + arguments: [chunks, line] + }), + new vscode.CodeLens(chunkRange, { + title: 'Run Current & Below', + tooltip: 'Run current and all chunks below', + command: 'r.runCurrentAndBelowChunks', + arguments: [chunks, line] + }), + new vscode.CodeLens(chunkRange, { + title: 'Run Below', + tooltip: 'Run all chunks below', + command: 'r.runBelowChunks', + arguments: [chunks, line] + }), + new vscode.CodeLens(chunkRange, { + title: 'Run Previous', + tooltip: 'Run previous chunk', + command: 'r.runPreviousChunk', + arguments: [chunks, line] + }), + new vscode.CodeLens(chunkRange, { + title: 'Run Next', + tooltip: 'Run next chunk', + command: 'r.runNextChunk', + arguments: [chunks, line] + }), + new vscode.CodeLens(chunkRange, { + title: 'Run All', + tooltip: 'Run all chunks', + command: 'r.runAllChunks', + arguments: [chunks] + }), + new vscode.CodeLens(chunkRange, { + title: 'Go Previous', + tooltip: 'Go to previous chunk', + command: 'r.goToPreviousChunk', + arguments: [chunks, line] + }), + new vscode.CodeLens(chunkRange, { + title: 'Go Next', + tooltip: 'Go to next chunk', + command: 'r.goToNextChunk', + arguments: [chunks, line] + }), + new vscode.CodeLens(chunkRange, { + title: 'Select Chunk', + tooltip: 'Select current chunk', + command: 'r.selectCurrentChunk', + arguments: [chunks, line] + }), + ); + } + } + + // For default options, both options and sort order are based on options specified in package.json. + // For user-specified options, both options and sort order are based on options specified in settings UI or settings.json. + const rmdCodeLensCommands: string[] = config().get('rmarkdown.codeLensCommands', []); + codeLenses = codeLenses. + filter(e => e.command && rmdCodeLensCommands.includes(e.command.command)). + sort(function (a, b) { + if (!a.command || !b.command) { return 0; } + const sorted = rmdCodeLensCommands.indexOf(a.command.command) - + rmdCodeLensCommands.indexOf(b.command.command); + return sorted; + }); + + return codeLenses; +} diff --git a/src/rmarkdown/draft.ts b/src/rmarkdown/draft.ts new file mode 100644 index 000000000..1c0b7c001 --- /dev/null +++ b/src/rmarkdown/draft.ts @@ -0,0 +1,162 @@ +import { QuickPickItem, QuickPickOptions, Uri, window, workspace, env } from 'vscode'; +import { extensionContext } from '../extension'; +import { executeRCommand, getCurrentWorkspaceFolder, getRpath, ToRStringLiteral, spawnAsync, getConfirmation, catchAsError } from '../util'; +import * as cp from 'child_process'; +import * as path from 'path'; +import * as fs from 'fs'; +import * as os from 'os'; + +interface TemplateInfo { + id: string; + package: string; + name: string; + description: string; + create_dir: boolean; +} + +interface TemplateItem extends QuickPickItem { + info: TemplateInfo; +} + +async function getTemplateItems(cwd: string): Promise { + const lim = '---vsc---'; + const rPath = await getRpath(); + if (!rPath) { + return undefined; + } + const options: cp.CommonOptions = { + cwd: cwd, + env: { + ...process.env, + VSCR_LIM: lim + } + }; + + const rScriptFile = extensionContext.asAbsolutePath('R/rmarkdown/templates.R'); + const args = [ + '--silent', + '--no-echo', + '--no-save', + '--no-restore', + '-f', + rScriptFile + ]; + + try { + const result = await spawnAsync(rPath, args, options); + if (result.status !== 0) { + throw result.error || new Error(result.stderr); + } + const re = new RegExp(`${lim}(.*)${lim}`, 'ms'); + const match = re.exec(result.stdout); + if (!match || match.length !== 2) { + throw new Error('Could not parse R output.'); + } + const json = match[1]; + const templates = JSON.parse(json) || []; + const items = templates.map((x) => { + return { + alwaysShow: false, + description: `{${x.package}}`, + label: x.name + (x.create_dir ? ' $(new-folder)' : ''), + detail: x.description, + picked: false, + info: x + }; + }); + return items; + } catch (e) { + console.log(e); + void window.showErrorMessage(catchAsError(e).message); + return undefined; + } +} + +async function launchTemplatePicker(cwd: string): Promise { + const options: QuickPickOptions = { + matchOnDescription: true, + matchOnDetail: true, + canPickMany: false, + ignoreFocusOut: false, + placeHolder: '', + onDidSelectItem: undefined + }; + + const items = await getTemplateItems(cwd); + + if (items) { + if (items.length > 0) { + const selection = await window.showQuickPick(items, options); + return selection; + } else { + void window.showInformationMessage('No templates found. Would you like to browse the wiki page for R packages that provide R Markdown templates?', 'Yes', 'No') + .then((select: string | undefined) => { + if (select === 'Yes') { + void env.openExternal(Uri.parse('https://github.com/REditorSupport/vscode-R/wiki/R-Markdown#templates')); + } + }); + } + } + return undefined; +} + +async function makeDraft(file: string, template: TemplateItem, cwd: string): Promise { + const fileString = ToRStringLiteral(file, ''); + const cmd = `cat(normalizePath(rmarkdown::draft(file='${fileString}', template='${template.info.id}', package='${template.info.package}', edit=FALSE)))`; + return await executeRCommand(cmd, cwd, (e: Error) => { + void window.showErrorMessage(e.message); + return ''; + }); +} + +export async function newDraft(): Promise { + const cwd = getCurrentWorkspaceFolder()?.uri.fsPath ?? os.homedir(); + const template = await launchTemplatePicker(cwd); + if (!template) { + return; + } + + if (template.info.create_dir) { + let defaultPath = path.join(cwd, 'draft'); + let i = 1; + while (fs.existsSync(defaultPath)) { + defaultPath = path.join(cwd, `draft_${++i}`); + } + const uri = await window.showSaveDialog({ + defaultUri: Uri.file(defaultPath), + filters: { + 'Folder': [''] + }, + saveLabel: 'Create Folder', + title: 'R Markdown: New Draft' + }); + + if (uri) { + const parsedPath = path.parse(uri.fsPath); + const dir = path.join(parsedPath.dir, parsedPath.name); + if (fs.existsSync(dir)) { + if (await getConfirmation(`Folder already exists. Are you sure you want to replace the folder?`)) { + fs.rmdirSync(dir, { recursive: true }); + } else { + return; + } + } + + const draftPath = await makeDraft(uri.fsPath, template, cwd); + if (draftPath) { + await workspace.openTextDocument(draftPath) + .then(document => window.showTextDocument(document)); + } + } + } else { + const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'vscode-R-')); + const tempFile = path.join(tempDir, 'draft.Rmd'); + const draftPath = await makeDraft(tempFile, template, cwd); + if (draftPath) { + const text = fs.readFileSync(draftPath, 'utf8'); + await workspace.openTextDocument({ language: 'rmd', content: text }) + .then(document => window.showTextDocument(document)); + } + fs.rmdirSync(tempDir, { recursive: true }); + } +} diff --git a/src/rmarkdown/index.ts b/src/rmarkdown/index.ts new file mode 100644 index 000000000..e82418fe3 --- /dev/null +++ b/src/rmarkdown/index.ts @@ -0,0 +1,184 @@ +import * as vscode from 'vscode'; +import { config } from '../util'; +import { + shouldDisplayChunkOptions, getChunks, getCurrentChunk, + getCodeLenses, isRDocument, + type RMarkdownChunk +} from './chunks'; + +// reexports +export { knitDir, RMarkdownKnitManager } from './knit'; +export { RMarkdownPreviewManager } from './preview'; +export { newDraft } from './draft'; +export { getChunks, runCurrentChunk, runCurrentChunkAndMove, runPreviousChunk, runNextChunk, runAboveChunks, runBelowChunks, runCurrentAndBelowChunks, runAllChunks, goToPreviousChunk, goToNextChunk, selectCurrentChunk } from './chunks'; + +export class RMarkdownCodeLensProvider implements vscode.CodeLensProvider { + private _onDidChangeCodeLenses: vscode.EventEmitter = new vscode.EventEmitter(); + private readonly decoration: vscode.TextEditorDecorationType; + public readonly onDidChangeCodeLenses: vscode.Event = this._onDidChangeCodeLenses.event; + private readonly currentCellTop: vscode.TextEditorDecorationType; + private readonly currentCellBottom: vscode.TextEditorDecorationType; + private onDidChangeTextEditorSelectionHandler: vscode.Disposable | undefined; + + constructor() { + this.decoration = vscode.window.createTextEditorDecorationType({ + isWholeLine: true, + backgroundColor: config().get('rmarkdown.chunkBackgroundColor'), + }); + // From https://github.com/microsoft/vscode-jupyter/blob/f8c0f925d855a45240fd06875b17216e47eb08f8/src/interactive-window/editor-integration/decorator.ts#L84 + this.currentCellTop = vscode.window.createTextEditorDecorationType({ + borderColor: new vscode.ThemeColor('interactive.activeCodeBorder'), + borderWidth: '2px 0px 0px 0px', + borderStyle: 'solid', + isWholeLine: true + }); + this.currentCellBottom = vscode.window.createTextEditorDecorationType({ + borderColor: new vscode.ThemeColor('interactive.activeCodeBorder'), + borderWidth: '0px 0px 1px 0px', + borderStyle: 'solid', + isWholeLine: true + }); + + // Register the event listener and store the disposable + this.onDidChangeTextEditorSelectionHandler = vscode.window.onDidChangeTextEditorSelection( + () => this.onDidChangeTextEditorSelection() + ); + } + + // Event handler for text editor selection change + private onDidChangeTextEditorSelection() { + // Get the active editor + const editor = vscode.window.activeTextEditor; + + if (editor) { + const document = editor.document; + const chunks = getChunks(document); + + // Call highlightCurrentChunk with the updated chunks and document + this.highlight(chunks, document); + } + } + + private highlightCurrentChunk(chunks: RMarkdownChunk[], document: vscode.TextDocument) { + for (const editor of vscode.window.visibleTextEditors) { + if (editor.document.uri.toString() === document.uri.toString()) { + const lines = document.getText().split(/\r?\n/); + const currentLine = editor.selection.active.line; + const currentChunk = getCurrentChunk(chunks, currentLine); + + if (currentChunk) { + // set top border + const currentChunkStart = new vscode.Range( + new vscode.Position(currentChunk.startLine, 0), + new vscode.Position(currentChunk.startLine, lines[currentChunk.startLine].length) + ); + editor.setDecorations(this.currentCellTop, [currentChunkStart]); + + // set bottom border + const currentChunkEnd = new vscode.Range( + new vscode.Position(currentChunk.endLine, 0), + new vscode.Position(currentChunk.endLine, lines[currentChunk.endLine].length) + ); + editor.setDecorations(this.currentCellBottom, [currentChunkEnd]); + } + } + } + } + + private highlightChunks(chunks: RMarkdownChunk[], document: vscode.TextDocument) { + const chunkRanges = chunks.map((chunk) => chunk.chunkRange); + for (const editor of vscode.window.visibleTextEditors) { + if (editor.document.uri.toString() === document.uri.toString()) { + editor.setDecorations(this.decoration, chunkRanges); + } + } + } + + private highlight(chunks: RMarkdownChunk[], document: vscode.TextDocument) { + if (!chunks) { + return; + } + + // Highlight differently for `.R` and `.Rmd` files + const isRDoc = isRDocument(document); + if (isRDoc) { + this.highlightCurrentChunk(chunks, document); + } else { + this.highlightChunks(chunks, document); + } + } + + public provideCodeLenses(document: vscode.TextDocument, token: vscode.CancellationToken): vscode.CodeLens[] | Thenable { + + const chunks = getChunks(document); + + // Highlight chunks + this.highlight(chunks, document); + + // Loop through chunks and setup + const codeLenses = getCodeLenses( + chunks, token + ); + + return codeLenses; + } + public resolveCodeLens(codeLens: vscode.CodeLens): vscode.CodeLens { + return codeLens; + } + + // Clean-up + dispose() { + // Unregister the event listener when the provider is disposed + if (this.onDidChangeTextEditorSelectionHandler) { + this.onDidChangeTextEditorSelectionHandler.dispose(); + } + } +} + +export class RMarkdownCompletionItemProvider implements vscode.CompletionItemProvider { + + // obtained from R code + // paste0("[", paste0(paste0("'", names(knitr:: opts_chunk$merge(NULL)), "'"), collapse = ", "), "]") + public readonly chunkOptions = ['eval', 'echo', 'results', 'tidy', 'tidy.opts', 'collapse', + 'prompt', 'comment', 'highlight', 'strip.white', 'size', 'background', + 'cache', 'cache.path', 'cache.vars', 'cache.lazy', 'dependson', + 'autodep', 'cache.rebuild', 'fig.keep', 'fig.show', 'fig.align', + 'fig.path', 'dev', 'dev.args', 'dpi', 'fig.ext', 'fig.width', + 'fig.height', 'fig.env', 'fig.cap', 'fig.scap', 'fig.lp', 'fig.subcap', + 'fig.pos', 'out.width', 'out.height', 'out.extra', 'fig.retina', + 'external', 'sanitize', 'interval', 'aniopts', 'warning', 'error', + 'message', 'render', 'ref.label', 'child', 'engine', 'split', + 'include', 'purl']; + public readonly chunkOptionCompletionItems: vscode.CompletionItem[]; + + constructor() { + this.chunkOptionCompletionItems = this.chunkOptions.map((x: string) => { + const item = new vscode.CompletionItem(`${x}`); + item.insertText = `${x}=`; + return item; + }); + } + + public provideCompletionItems(document: vscode.TextDocument, position: vscode.Position): vscode.CompletionItem[] | undefined { + if (shouldDisplayChunkOptions(document, position)) { + return this.chunkOptionCompletionItems; + } + + return undefined; + } +} + +// Fold code chunks +export class RChunkFoldingProvider implements vscode.FoldingRangeProvider { + constructor() { this; } + + provideFoldingRanges(document: vscode.TextDocument): vscode.ProviderResult { + const chunks = getChunks(document); + if (chunks) { + return chunks.map((chunk) => { + return new vscode.FoldingRange(chunk.startLine, chunk.endLine, vscode.FoldingRangeKind.Region); + }); + } + return undefined; + } +} diff --git a/src/rmarkdown/knit.ts b/src/rmarkdown/knit.ts new file mode 100644 index 000000000..1268f18b0 --- /dev/null +++ b/src/rmarkdown/knit.ts @@ -0,0 +1,294 @@ +import * as util from '../util'; +import * as vscode from 'vscode'; +import * as fs from 'fs-extra'; +import * as path from 'path'; +import * as yaml from 'js-yaml'; + +import { RMarkdownManager, KnitWorkingDirectory } from './manager'; +import { runTextInTerm } from '../rTerminal'; +import { extensionContext, rmdPreviewManager } from '../extension'; +import { DisposableProcess } from '../util'; + +export let knitDir: KnitWorkingDirectory | undefined; + +interface IKnitQuickPickItem { + label: string, + description: string, + detail: string, + value: KnitWorkingDirectory +} + +interface IYamlFrontmatter { + title?: string, + author?: string, + knit?: string, + site?: string, + [key: string]: unknown +} + +export class RMarkdownKnitManager extends RMarkdownManager { + constructor() { + super(); + knitDir = util.config().get('rmarkdown.knit.defaults.knitWorkingDirectory') ?? undefined; + } + + + private async renderDocument(rDocumentPath: string, docPath: string, docName: string, yamlParams: IYamlFrontmatter, outputFormat?: string): Promise { + const openOutfile: boolean = util.config().get('rmarkdown.knit.openOutputFile') ?? false; + const knitWorkingDir = this.getKnitDir(knitDir, docPath); + const knitWorkingDirText = knitWorkingDir ? `${knitWorkingDir}` : ''; + const knitCommand = await this.getKnitCommand(yamlParams, rDocumentPath, outputFormat); + if (!knitCommand) { + return; + } + this.rPath = await util.getRpath(); + + const lim = '<<>>'; + const re = new RegExp(`.*${lim}(.*)${lim}.*`, 'gms'); + const scriptValues = { + 'VSCR_KNIT_DIR': knitWorkingDirText, + 'VSCR_LIM': lim, + 'VSCR_KNIT_COMMAND': knitCommand + }; + + const callback = (dat: string) => { + const outputUrl = re.exec(dat)?.[0]?.replace(re, '$1'); + if (!outputUrl) { + return false; + } + if (openOutfile) { + const outFile = vscode.Uri.file(outputUrl); + if (fs.existsSync(outFile.fsPath)) { + void vscode.commands.executeCommand('vscode.open', outFile); + } else { + void vscode.window.showWarningMessage(`Could not find the output file at path: "${outFile.fsPath}"`); + } + } + return true; + }; + + if (util.config().get('rmarkdown.knit.focusOutputChannel')) { + this.rMarkdownOutput.show(true); + } + + return await this.knitWithProgress( + { + workingDirectory: knitWorkingDirText, + fileName: docName, + filePath: rDocumentPath, + scriptArgs: scriptValues, + scriptPath: extensionContext.asAbsolutePath('R/rmarkdown/knit.R'), + rCmd: knitCommand, + rOutputFormat: outputFormat, + callback: callback + } + ); + + } + + private getYamlFrontmatter(docPath: string): IYamlFrontmatter { + const text = fs.readFileSync(docPath, 'utf8'); + const lines = text.split('\n'); + let startLine = -1; + let endLine = -1; + for (let i = 0; i < lines.length; i++) { + if (/\S/.test(lines[i])) { + if (startLine < 0) { + if (lines[i].startsWith('---')) { + startLine = i; + } else { + break; + } + } else { + if (lines[i].startsWith('---')) { + endLine = i; + break; + } + } + } + } + + let yamlText: string | undefined = undefined; + if (startLine + 1 < endLine) { + yamlText = lines.slice(startLine + 1, endLine).join('\n'); + } + + let paramObj: IYamlFrontmatter = {}; + if (yamlText) { + try { + paramObj = yaml.load( + yamlText + ) as IYamlFrontmatter; + } catch (e) { + console.error(`Could not parse YAML frontmatter for "${docPath}". Error: ${String(e)}`); + } + } + + return paramObj; + } + + private async getKnitCommand(yamlParams: IYamlFrontmatter, docPath: string, outputFormat?: string): Promise { + let knitCommand: string; + + if (!yamlParams?.['site']) { + yamlParams['site'] = await this.findSiteParam(); + } + + // precedence: + // knit > site > configuration + if (yamlParams?.['knit']) { + const knitParam = yamlParams['knit'].trim(); + knitCommand = outputFormat ? + `${knitParam}(${docPath}, output_format = '${outputFormat}')` : + `${knitParam}(${docPath})`; + } else if (!this.isREADME(docPath) && yamlParams?.['site']) { + knitCommand = outputFormat ? + `rmarkdown::render_site(${docPath}, output_format = '${outputFormat}')` : + `rmarkdown::render_site(${docPath})`; + } else { + const cmd = util.config().get('rmarkdown.knit.command'); + if (!cmd) { + return; + } + knitCommand = outputFormat ? + `${cmd}(${docPath}, output_format = '${outputFormat}')` : + `${cmd}(${docPath})`; + } + + return knitCommand.replace(/['"]/g, '\''); + } + + // check if the workspace of the document is a R Markdown site. + // the definition of what constitutes an R Markdown site differs + // depending on the type of R Markdown site (i.e., "simple" vs. blogdown sites) + private async findSiteParam(): Promise { + const wad = vscode.window.activeTextEditor?.document.uri.fsPath; + if (!wad) { + return; + } + const rootFolder = vscode.workspace.workspaceFolders?.[0]?.uri?.fsPath ?? path.dirname(wad); + const indexFile = (await vscode.workspace.findFiles(new vscode.RelativePattern(rootFolder, 'index.{Rmd,rmd, md}'), null, 1))?.[0]; + const siteRoot = path.join(path.dirname(wad), '_site.yml'); + + // 'Simple' R Markdown websites require all docs to be in the root folder + if (fs.existsSync(siteRoot)) { + return 'rmarkdown::render_site'; + // Other generators may allow for docs in subdirs + } else if (indexFile) { + const indexData = this.getYamlFrontmatter(indexFile.fsPath); + if (indexData?.['site']) { + return indexData['site']; + } + } + + return undefined; + } + + // readme files should not be knitted via render_site + private isREADME(docPath: string) { + return !!path.basename(docPath).includes('README'); + } + + // alters the working directory for evaluating chunks + public setKnitDir(): void { + const textEditor = vscode.window.activeTextEditor; + const currentDocumentWorkspacePath: string | undefined = textEditor ? vscode.workspace.getWorkspaceFolder(textEditor.document.uri)?.uri.fsPath : undefined; + const currentDocumentFolderPath: string | undefined = textEditor ? path.dirname(textEditor.document.uri.fsPath) : undefined; + const items: IKnitQuickPickItem[] = []; + + if (currentDocumentWorkspacePath) { + items.push( + { + label: (knitDir === KnitWorkingDirectory.workspaceRoot ? '$(check)' : '') + KnitWorkingDirectory.workspaceRoot, + value: KnitWorkingDirectory.workspaceRoot, + detail: 'Use the workspace root as the knit working directory', + description: currentDocumentWorkspacePath ?? currentDocumentFolderPath ?? 'No available workspace' + } + ); + } + + if (currentDocumentFolderPath && currentDocumentFolderPath !== '.') { + items.push( + { + label: (knitDir === KnitWorkingDirectory.documentDirectory ? '$(check)' : '') + KnitWorkingDirectory.documentDirectory, + value: KnitWorkingDirectory.documentDirectory, + detail: 'Use the document\'s directory as the knit working directory', + description: currentDocumentFolderPath ?? 'No folder available' + + } + ); + } + + if (items.length > 0) { + void vscode.window.showQuickPick( + items, + { + title: 'Set knit working directory', + canPickMany: false + } + ).then(async choice => { + if (choice?.value && knitDir !== choice.value) { + knitDir = choice.value; + await rmdPreviewManager?.updatePreview(); + } + }); + } else { + void vscode.window.showInformationMessage('Cannot set knit directory for untitled documents.'); + } + + } + + public async knitRmd(echo: boolean, outputFormat?: string): Promise { + const textEditor = vscode.window.activeTextEditor; + if (!textEditor) { + void vscode.window.showWarningMessage('No text editor active.'); + return; + } + + const wad: vscode.TextDocument = textEditor.document; + + // handle untitled rmd + if (textEditor.document.isUntitled) { + void vscode.window.showWarningMessage('Cannot knit an untitled file. Please save the document.'); + await vscode.commands.executeCommand('workbench.action.files.save').then(() => { + if (!textEditor.document.isUntitled) { + void this.knitRmd(echo, outputFormat); + } + }); + return; + } + + const isSaved = await util.saveDocument(wad); + if (!isSaved) { + return; + } + let rDocumentPath = util.ToRStringLiteral(wad.fileName, '"'); + + if (echo) { + rDocumentPath = [rDocumentPath, 'echo = TRUE'].join(', '); + } + + // allow users to opt out of background process + if (util.config().get('rmarkdown.knit.useBackgroundProcess')) { + const busyPath = wad.uri.fsPath + (outputFormat ?? ''); + if (this.busyUriStore.has(busyPath)) { + return; + } + this.busyUriStore.add(busyPath); + await this.renderDocument( + rDocumentPath, + wad.uri.fsPath, + path.basename(wad.uri.fsPath), + this.getYamlFrontmatter(wad.uri.fsPath), + outputFormat + ); + this.busyUriStore.delete(busyPath); + } else { + if (outputFormat === undefined) { + void runTextInTerm(`rmarkdown::render(${rDocumentPath})`); + } else { + void runTextInTerm(`rmarkdown::render(${rDocumentPath}, '${outputFormat}')`); + } + } + } +} diff --git a/src/rmarkdown/manager.ts b/src/rmarkdown/manager.ts new file mode 100644 index 000000000..e062955ea --- /dev/null +++ b/src/rmarkdown/manager.ts @@ -0,0 +1,214 @@ +import * as util from '../util'; +import * as vscode from 'vscode'; +import * as cp from 'child_process'; +import path = require('path'); +import { DisposableProcess, spawn } from '../util'; + +export enum KnitWorkingDirectory { + documentDirectory = 'document directory', + workspaceRoot = 'workspace root', +} + +export interface IKnitRejection { + cp: DisposableProcess; + wasCancelled: boolean; +} + +const rMarkdownOutput: vscode.OutputChannel = vscode.window.createOutputChannel('R Markdown'); + +interface IKnitArgs { + workingDirectory: string; + filePath: string; + fileName: string; + scriptArgs: Record; + scriptPath: string; + rCmd?: string; + rOutputFormat?: string; + callback: (dat: string, childProcess?: util.DisposableProcess) => boolean; + onRejection?: (filePath: string, rejection: IKnitRejection) => unknown; +} + +export abstract class RMarkdownManager { + protected rPath: string | undefined = undefined; + protected rMarkdownOutput: vscode.OutputChannel = rMarkdownOutput; + // uri that are in the process of knitting + // so that we can't spam the knit/preview button + protected busyUriStore: Set = new Set(); + + protected getKnitDir(knitDir: string | undefined, docPath: string): string | undefined { + switch (knitDir) { + // the directory containing the R Markdown document + case KnitWorkingDirectory.documentDirectory: { + return path.dirname(docPath)?.replace(/\\/g, '/')?.replace(/['"]/g, '\\"') ?? undefined; + } + // the root of the current workspace + case KnitWorkingDirectory.workspaceRoot: { + const currentDocumentWorkspace = vscode.workspace.getWorkspaceFolder(vscode.Uri.file(docPath) ?? vscode.window.activeTextEditor?.document?.uri)?.uri?.fsPath ?? undefined; + return currentDocumentWorkspace?.replace(/\\/g, '/')?.replace(/['"]/g, '\\"') ?? undefined; + } + // the working directory of the attached terminal, NYI + // case 'current directory': { + // return NULL + // } + default: return undefined; + } + } + + protected async knitDocument(args: IKnitArgs, token?: vscode.CancellationToken, progress?: vscode.Progress): Promise { + // vscode.Progress auto-increments progress, so we use this + // variable to set progress to a specific number + let currentProgress = 0; + let printOutput = true; + + return await new Promise( + (resolve, reject) => { + const scriptArgs = args.scriptArgs; + const scriptPath = args.scriptPath; + const fileName = args.fileName; + // const cmd = `${this.rPath} --silent --no-echo --no-save --no-restore -f "${scriptPath}"`; + const cpArgs = [ + '--silent', + '--no-echo', + '--no-save', + '--no-restore', + '-f', + scriptPath + ]; + // When there's no LANG variable, we should try to set to a UTF-8 compatible one, as R relies + // on locale setting (based on LANG) to render certain characters. + // See https://github.com/REditorSupport/vscode-R/issues/933 + const env = process.env; + if (env.LANG === undefined) { + env.LANG = 'en_US.UTF-8'; + } + const processOptions: cp.SpawnOptions = { + env: { + ...env, + ...scriptArgs + }, + cwd: args.workingDirectory, + }; + + let childProcess: DisposableProcess | undefined = undefined; + try { + if (!this.rPath) { + throw new Error('R path not defined'); + } + + childProcess = spawn(this.rPath, cpArgs, processOptions, () => { + rMarkdownOutput.appendLine('[VSC-R] terminating R process'); + printOutput = false; + }); + progress?.report({ + increment: 0, + message: '0%' + }); + } catch (e: unknown) { + console.warn(`[VSC-R] error: ${e as string}`); + reject({ cp: childProcess, wasCancelled: false }); + return; + } + + this.rMarkdownOutput.appendLine(`[VSC-R] ${fileName} process started`); + + if (args.rCmd) { + this.rMarkdownOutput.appendLine(`==> ${args.rCmd}`); + } + + childProcess.stdout.on('data', + (data: Buffer) => { + const dat = data.toString('utf8'); + if (printOutput) { + this.rMarkdownOutput.appendLine(dat); + + } + const percentRegex = /[0-9]+(?=%)/g; + const divisionRegex = /([0-9]+)\/([0-9]+)/g; + const percentRegOutput = dat.match(percentRegex); + if (percentRegOutput) { + for (const item of percentRegOutput) { + const perc = Number(item); + progress?.report( + { + increment: perc - currentProgress, + message: `${perc}%` + } + ); + currentProgress = perc; + } + } else { + const divisionRegOutput = divisionRegex.exec(dat); + if (divisionRegOutput) { + const perc = Math.ceil(parseInt(divisionRegOutput[1]) / parseInt(divisionRegOutput[2]) * 100); + progress?.report( + { + increment: perc - currentProgress, + message: `${perc}%` + } + ); + currentProgress = perc; + } + } + + if (token?.isCancellationRequested) { + if (childProcess) { + resolve(childProcess); + } + } else { + if (args.callback(dat, childProcess)) { + if (childProcess) { + resolve(childProcess); + } + } + } + } + ); + + childProcess.stderr.on('data', (data: Buffer) => { + const dat = data.toString('utf8'); + if (printOutput) { + this.rMarkdownOutput.appendLine(dat); + } + }); + + childProcess.on('exit', (code, signal) => { + this.rMarkdownOutput.appendLine(`[VSC-R] ${fileName} process exited ` + + (signal ? `from signal '${signal}'` : `with exit code ${code || 'null'}`)); + if (code !== 0) { + reject({ cp: childProcess, wasCancelled: false }); + } + }); + + token?.onCancellationRequested(() => { + reject({ cp: childProcess, wasCancelled: true }); + }); + } + ); + } + + protected async knitWithProgress(args: IKnitArgs): Promise { + let childProcess: DisposableProcess | undefined = undefined; + await util.doWithProgress( + (async ( + token: vscode.CancellationToken | undefined, + progress: vscode.Progress<{ + message?: string | undefined; + increment?: number | undefined; + }> | undefined) => { + childProcess = await this.knitDocument(args, token, progress) as DisposableProcess; + }), + vscode.ProgressLocation.Notification, + `Knitting ${args.fileName} ${args.rOutputFormat ? 'to ' + args.rOutputFormat : ''} `, + true + ).catch((rejection: IKnitRejection) => { + if (!rejection.wasCancelled) { + void vscode.window.showErrorMessage('There was an error in knitting the document. Please check the R Markdown output stream.'); + this.rMarkdownOutput.show(true); + } + // this can occur when a successfuly knitted document is later altered (while still being previewed) and subsequently fails to knit + args?.onRejection?.(args.filePath, rejection); + rejection.cp?.dispose(); + }); + return childProcess; + } +} diff --git a/src/rmarkdown/preview.ts b/src/rmarkdown/preview.ts new file mode 100644 index 000000000..b9d5984d6 --- /dev/null +++ b/src/rmarkdown/preview.ts @@ -0,0 +1,428 @@ +import * as vscode from 'vscode'; +import * as fs from 'fs-extra'; +import * as cheerio from 'cheerio'; + +import path = require('path'); +import crypto = require('crypto'); + + +import { config, readContent, setContext, escapeHtml, UriIcon, saveDocument, getRpath, DisposableProcess } from '../util'; +import { extensionContext, tmpDir } from '../extension'; +import { knitDir } from './knit'; +import { RMarkdownManager } from './manager'; + +class RMarkdownPreview extends vscode.Disposable { + title: string; + cp: DisposableProcess | undefined; + panel: vscode.WebviewPanel; + resourceViewColumn: vscode.ViewColumn; + outputUri: vscode.Uri; + htmlDarkContent: string | undefined; + htmlLightContent: string | undefined; + fileWatcher: fs.FSWatcher | undefined; + autoRefresh: boolean; + mtime: number; + isRendering: boolean; + + constructor(title: string, cp: DisposableProcess | undefined, panel: vscode.WebviewPanel, + resourceViewColumn: vscode.ViewColumn, outputUri: vscode.Uri, filePath: string, + RMarkdownPreviewManager: RMarkdownPreviewManager, useCodeTheme: boolean, autoRefresh: boolean) { + super(() => { + this.cp?.dispose(); + this.panel?.dispose(); + this.fileWatcher?.close(); + fs.removeSync(this.outputUri.fsPath); + }); + + this.title = title; + this.cp = cp; + this.panel = panel; + this.resourceViewColumn = resourceViewColumn; + this.outputUri = outputUri; + this.autoRefresh = autoRefresh; + this.mtime = fs.statSync(filePath).mtime.getTime(); + this.isRendering = false; + void this.refreshContent(useCodeTheme); + this.startFileWatcher(RMarkdownPreviewManager, filePath); + } + + public styleHtml(useCodeTheme: boolean) { + if (useCodeTheme) { + this.panel.webview.html = this.htmlDarkContent ?? ''; + } else { + this.panel.webview.html = this.htmlLightContent ?? ''; + } + } + + public async refreshContent(useCodeTheme: boolean) { + this.getHtmlContent(await readContent(this.outputUri.fsPath, 'utf8') ?? ''); + this.styleHtml(useCodeTheme); + } + + private startFileWatcher(RMarkdownPreviewManager: RMarkdownPreviewManager, filePath: string) { + let fsTimeout: NodeJS.Timeout | null; + const fileWatcher = fs.watch(filePath, {}, () => { + const mtime = fs.statSync(filePath).mtime.getTime(); + if (this.autoRefresh && !this.isRendering && !fsTimeout && mtime !== this.mtime) { + fsTimeout = setTimeout(() => { fsTimeout = null; }, 1000); + this.mtime = mtime; + void RMarkdownPreviewManager.updatePreview(this); + } + }); + this.fileWatcher = fileWatcher; + } + + private getHtmlContent(htmlContent: string): void { + let content = htmlContent.replace(/<(\w+)\s+(href|src)="(?!(\w+:)|#)/g, + `<$1 $2="${String(this.panel.webview.asWebviewUri(vscode.Uri.file(tmpDir())))}/`); + + const re = new RegExp('.*', 'ms'); + const isHtml = !!re.exec(content); + + if (!isHtml) { + const html = escapeHtml(content); + content = `
${html}
`; + } + + const $ = cheerio.load(content); + this.htmlLightContent = $.html(); + + const zoom = config().get('rmarkdown.preview.zoom', 1); + + // make the output chunks a little lighter to stand out + let chunkCol = String(config().get('rmarkdown.chunkBackgroundColor')); + + let outCol: string; + if (chunkCol) { + const colReg = /[0-9.]+/g; + const regOut = chunkCol.match(colReg); + if (regOut) { + outCol = `rgba(${regOut[0] ?? 128}, ${regOut[1] ?? 128}, ${regOut[2] ?? 128}, ${Math.max(0, Number(regOut[3] ?? 0.1) - 0.05)})`; + } else { + outCol = 'rgba(128, 128, 128, 0.05)'; + } + } else { + chunkCol = 'rgba(128, 128, 128, 0.1)'; + outCol = 'rgba(128, 128, 128, 0.05)'; + } + + const style = + ` + `; + $('head').append(style); + this.htmlDarkContent = $.html(); + } +} + +class RMarkdownPreviewStore extends vscode.Disposable { + private store: Map = new Map(); + + constructor() { + super((): void => { + for (const preview of this.store) { + preview[1].dispose(); + } + this.store.clear(); + }); + } + + public add(filePath: string, preview: RMarkdownPreview): Map { + return this.store.set(filePath, preview); + } + + // dispose child and remove it from set + public delete(filePath: string): boolean { + this.store.get(filePath)?.dispose(); + return this.store.delete(filePath); + } + + public get(filePath: string): RMarkdownPreview | undefined { + return this.store.get(filePath); + } + + public getFilePath(preview: RMarkdownPreview): string | undefined { + for (const _preview of this.store) { + if (_preview[1] === preview) { + return _preview[0]; + } + } + return undefined; + } + + public has(filePath: string): boolean { + return this.store.has(filePath); + } + + [Symbol.iterator]() { + return this.store[Symbol.iterator](); + } +} + +export class RMarkdownPreviewManager extends RMarkdownManager { + // the currently selected RMarkdown preview + private activePreview: { filePath: string | null, preview: RMarkdownPreview | null, title: string | null } = { filePath: null, preview: null, title: null }; + // store of all open RMarkdown previews + private previewStore: RMarkdownPreviewStore = new RMarkdownPreviewStore; + private useCodeTheme = true; + + constructor() { + super(); + extensionContext.subscriptions.push(this.previewStore); + } + + + public async previewRmd(viewer: vscode.ViewColumn, uri?: vscode.Uri): Promise { + const textEditor = vscode.window.activeTextEditor; + if (!textEditor) { + void vscode.window.showErrorMessage('No text editor active.'); + return; + } + + const filePath = uri ? uri.fsPath : textEditor.document.uri.fsPath; + const fileName = path.basename(filePath); + const currentViewColumn: vscode.ViewColumn = vscode.window.activeTextEditor?.viewColumn ?? vscode.ViewColumn.Active ?? vscode.ViewColumn.One; + + // handle untitled rmd files + if (!uri && textEditor.document.isUntitled) { + void vscode.window.showWarningMessage('Cannot knit an untitled file. Please save the document.'); + await vscode.commands.executeCommand('workbench.action.files.save').then(() => { + if (!textEditor.document.isUntitled) { + void this.previewRmd(viewer); + } + }); + return; + } + + const isSaved = uri ? + true : + await saveDocument(textEditor.document); + + if (!isSaved) { + return; + } + // don't knit if the current uri is already being knit + if (this.busyUriStore.has(filePath)) { + return; + } else if (this.previewStore.has(filePath)) { + this.previewStore.get(filePath)?.panel.reveal(); + } else { + this.busyUriStore.add(filePath); + await this.previewDocument(filePath, fileName, viewer, currentViewColumn); + this.busyUriStore.delete(filePath); + } + } + + public enableAutoRefresh(preview?: RMarkdownPreview): void { + if (preview) { + preview.autoRefresh = true; + } else if (this.activePreview?.preview) { + this.activePreview.preview.autoRefresh = true; + void setContext('r.rmarkdown.preview.autoRefresh', true); + } + } + + public disableAutoRefresh(preview?: RMarkdownPreview): void { + if (preview) { + preview.autoRefresh = false; + } else if (this.activePreview?.preview) { + this.activePreview.preview.autoRefresh = false; + void setContext('r.rmarkdown.preview.autoRefresh', false); + } + } + + public toggleTheme(): void { + this.useCodeTheme = !this.useCodeTheme; + for (const preview of this.previewStore) { + void preview[1].styleHtml(this.useCodeTheme); + } + } + + // show the source uri for the current preview. + // has a few idiosyncracies with view columns due to some limitations with + // vscode api. the view column will be set in order of priority: + // 1. the original document's view column when the preview button was pressed + // 2. the current webview's view column + // 3. the current active editor + // this is because we cannot tell the view column of a file if it is not visible + // (e.g., is an unopened tab) + public async showSource(): Promise { + if (this.activePreview?.filePath) { + await vscode.commands.executeCommand('vscode.open', vscode.Uri.file(this.activePreview.filePath), { + preserveFocus: false, + preview: false, + viewColumn: this.activePreview?.preview?.resourceViewColumn ?? this.activePreview?.preview?.panel.viewColumn ?? vscode.ViewColumn.Active + }); + } + } + + public async openExternalBrowser(): Promise { + if (this.activePreview.preview) { + await vscode.env.openExternal(this.activePreview.preview.outputUri); + } + } + + public async updatePreview(preview?: RMarkdownPreview): Promise { + const toUpdate = preview ?? this.activePreview.preview; + const previewUri = toUpdate ? this.previewStore.getFilePath(toUpdate) : undefined; + toUpdate?.cp?.dispose(); + + if (toUpdate && previewUri) { + toUpdate.isRendering = true; + const childProcess: DisposableProcess | void = await this.previewDocument(previewUri, toUpdate.title).catch(() => { + void vscode.window.showErrorMessage('There was an error in knitting the document. Please check the R Markdown output stream.'); + this.rMarkdownOutput.show(true); + this.previewStore.delete(previewUri); + }); + + if (childProcess) { + toUpdate.cp = childProcess; + } + + this.refreshPanel(toUpdate); + toUpdate.isRendering = false; + } + + } + + private async previewDocument(filePath: string, fileName?: string, viewer?: vscode.ViewColumn, currentViewColumn?: vscode.ViewColumn): Promise { + const knitWorkingDir = this.getKnitDir(knitDir, filePath); + const knitWorkingDirText = knitWorkingDir ? `${knitWorkingDir}` : ''; + this.rPath = await getRpath(); + + const lim = '<<>>'; + const re = new RegExp(`.*${lim}(.*)${lim}.*`, 'ms'); + const outputFile = path.join(tmpDir(), crypto.createHash('sha256').update(filePath).digest('hex') + '.html'); + const scriptValues = { + 'VSCR_KNIT_DIR': knitWorkingDirText, + 'VSCR_LIM': lim, + 'VSCR_FILE_PATH': filePath.replace(/\\/g, '/'), + 'VSCR_OUTPUT_FILE': outputFile.replace(/\\/g, '/'), + 'VSCR_TMP_DIR': tmpDir().replace(/\\/g, '/') + }; + + + const callback = (dat: string, childProcess?: DisposableProcess) => { + const outputUrl = re.exec(dat)?.[0]?.replace(re, '$1'); + if (outputUrl) { + if (viewer !== undefined && fileName) { + const autoRefresh = config().get('rmarkdown.preview.autoRefresh', false); + void this.openPreview( + vscode.Uri.file(outputUrl), + filePath, + fileName, + childProcess, + viewer, + currentViewColumn ?? vscode.ViewColumn.Active, + autoRefresh + ); + } + return true; + } + return false; + }; + + const onRejected = (filePath: string) => { + if (this.previewStore.has(filePath)) { + this.previewStore.delete(filePath); + } + }; + + if (knitWorkingDir && fileName) { + return await this.knitWithProgress( + { + workingDirectory: knitWorkingDir, + fileName: fileName, + filePath: filePath, + scriptPath: extensionContext.asAbsolutePath('R/rmarkdown/preview.R'), + scriptArgs: scriptValues, + rOutputFormat: 'html preview', + callback: callback, + onRejection: onRejected + } + ); + } + } + + private openPreview(outputUri: vscode.Uri, filePath: string, title: string, cp: DisposableProcess | undefined, viewer: vscode.ViewColumn, resourceViewColumn: vscode.ViewColumn, autoRefresh: boolean): void { + + const panel = vscode.window.createWebviewPanel( + 'previewRmd', + `Preview ${title}`, + { + preserveFocus: true, + viewColumn: viewer + }, + { + enableFindWidget: true, + enableScripts: true, + retainContextWhenHidden: true, + localResourceRoots: [vscode.Uri.file(tmpDir())], + }); + + panel.iconPath = new UriIcon('preview'); + + // Push the new rmd webview to the open proccesses array, + // to keep track of running child processes + // (primarily used in killing the child process, but also + // general state tracking) + const preview = new RMarkdownPreview( + title, + cp, + panel, + resourceViewColumn, + outputUri, + filePath, + this, + this.useCodeTheme, + autoRefresh + ); + this.previewStore.add(filePath, preview); + + // state change + panel.onDidDispose(() => { + // clear values + this.activePreview = this.activePreview?.preview === preview ? { filePath: null, preview: null, title: null } : this.activePreview; + void setContext('r.rmarkdown.preview.active', false); + this.previewStore.delete(filePath); + }); + + panel.onDidChangeViewState(({ webviewPanel }) => { + void setContext('r.rmarkdown.preview.active', webviewPanel.active); + if (webviewPanel.active) { + this.activePreview.preview = preview; + this.activePreview.filePath = filePath; + this.activePreview.title = title; + void setContext('r.rmarkdown.preview.autoRefresh', preview.autoRefresh); + } + }); + } + + private refreshPanel(preview: RMarkdownPreview): void { + void preview.refreshContent(this.useCodeTheme); + } +} + diff --git a/src/rstudioapi.ts b/src/rstudioapi.ts new file mode 100644 index 000000000..ac4d738e5 --- /dev/null +++ b/src/rstudioapi.ts @@ -0,0 +1,476 @@ +/* eslint-disable @typescript-eslint/no-unsafe-argument */ +/* eslint-disable @typescript-eslint/restrict-plus-operands */ +/* eslint-disable @typescript-eslint/restrict-template-expressions */ +/* eslint-disable @typescript-eslint/no-unsafe-return */ +/* eslint-disable @typescript-eslint/no-unsafe-assignment */ +/* eslint-disable @typescript-eslint/no-explicit-any */ +/* eslint-disable @typescript-eslint/explicit-module-boundary-types */ +/* eslint-disable @typescript-eslint/no-unsafe-member-access */ +import { + window, TextEditor, TextDocument, Uri, + workspace, WorkspaceEdit, Position, Range, Selection, + QuickPickItem, QuickPickOptions, ViewColumn +} from 'vscode'; +import { readJSON } from 'fs-extra'; +import * as path from 'path'; +import { sessionDir, sessionDirectoryExists, writeResponse, writeSuccessResponse } from './session'; +import { runTextInTerm, restartRTerminal, chooseTerminal } from './rTerminal'; +import { config } from './util'; + +let lastActiveTextEditor: TextEditor; + +export async function dispatchRStudioAPICall(action: string, args: any, sd: string): Promise { + + switch (action) { + case 'active_editor_context': { + await writeResponse(activeEditorContext(), sd); + break; + } + case 'insert_or_modify_text': { + await insertOrModifyText(args.query, args.id); + await writeSuccessResponse(sd); + break; + } + case 'replace_text_in_current_selection': { + await replaceTextInCurrentSelection(args.text, args.id); + await writeSuccessResponse(sd); + break; + } + case 'show_dialog': { + showDialog(args.message); + await writeSuccessResponse(sd); + break; + } + case 'navigate_to_file': { + await navigateToFile(args.file, args.line, args.column); + await writeSuccessResponse(sd); + break; + } + case 'set_selection_ranges': { + await setSelections(args.ranges, args.id); + await writeSuccessResponse(sd); + break; + } + case 'document_save': { + await documentSave(args.id); + await writeSuccessResponse(sd); + break; + } + case 'document_save_all': { + await documentSaveAll(); + await writeSuccessResponse(sd); + break; + } + case 'get_project_path': { + await writeResponse(projectPath(), sd); + break; + } + case 'document_context': { + await writeResponse(await documentContext(args.id), sd); + break; + } + case 'document_new': { + await documentNew(args.text, args.type, args.position); + await writeSuccessResponse(sd); + break; + } + case 'restart_r': { + await restartRTerminal(); + await writeSuccessResponse(sd); + break; + } + case 'send_to_console': { + await sendCodeToRTerminal(args.code, args.execute, args.focus); + await writeSuccessResponse(sd); + break; + } + default: + console.error(`[dispatchRStudioAPICall] Unsupported action: ${action}`); + } + +} + +//rstudioapi +export function activeEditorContext() { + // info returned from RStudio: + // list with: + // id + // path + // contents + // selection - a list of selections + const currentDocument = getLastActiveTextEditor().document; + return { + id: currentDocument.uri, + contents: currentDocument.getText(), + path: currentDocument.fileName, + selection: getLastActiveTextEditor().selections + }; +} + +export async function documentContext(id: string) { + const target = findTargetUri(id); + const targetDocument = await workspace.openTextDocument(target); + console.info(`[documentContext] getting context for: ${target.path}`); + return { + id: targetDocument.uri + }; +} + +export async function insertOrModifyText(query: any[], id: string | null = null) { + + + const target = findTargetUri(id); + const targetDocument = await workspace.openTextDocument(target); + console.info(`[insertTextAtPosition] inserting text into: ${target.path}`); + const edit = new WorkspaceEdit(); + + query.forEach((op) => { + assertSupportedEditOperation(op.operation); + + let editLocation: any; + const editText = normaliseEditText(op.text, op.location, op.operation, targetDocument); + + if (op.operation === 'insertText') { + editLocation = parsePosition(op.location, targetDocument); + console.info(`[insertTextAtPosition] inserting at: ${JSON.stringify(editLocation)}`); + console.info(`[insertTextAtPosition] inserting text: ${editText}`); + edit.insert(target, editLocation, editText); + } else { + editLocation = parseRange(op.location, targetDocument); + console.info(`[insertTextAtPosition] replacing at: ${JSON.stringify(editLocation)}`); + console.info(`[insertTextAtPosition] replacing with text: ${editText}`); + edit.replace(target, editLocation, editText); + } + }); + + void workspace.applyEdit(edit); +} + +export async function replaceTextInCurrentSelection(text: string, id: string): Promise { + const target = findTargetUri(id); + console.info(`[replaceTextInCurrentSelection] inserting: ${text} into ${target.path}`); + const edit = new WorkspaceEdit(); + edit.replace( + target, + getLastActiveTextEditor().selection, + text + ); + await workspace.applyEdit(edit); +} + +export function showDialog(message: string): void { + + void window.showInformationMessage(message); + +} + +export async function navigateToFile(file: string, line: number, column: number): Promise{ + + const targetDocument = await workspace.openTextDocument(Uri.file(file)); + const editor = await window.showTextDocument(targetDocument); + const targetPosition = parsePosition([line, column], targetDocument); + editor.selection = new Selection(targetPosition, targetPosition); + editor.revealRange(new Range(targetPosition, targetPosition)); +} + +export async function setSelections(ranges: number[][], id: string): Promise { + // Setting selections can only be done on TextEditors not TextDocuments, but + // it is the latter which are the things actually referred to by `id`. In + // VSCode it's not possible to get a list of the open text editors. it is not + // window.visibleTextEditors - this is only editors (tabs) with text showing. + // The only editors we know about are those that are visible and the last + // active (which may not be visible if it was overtaken by a WebViewPanel). + // This function looks to see if a text editor for the document id is amongst + // those known, and if not, it opens and shows that document, but in a + // texteditor 'beside' the current one. + // The rationale for this is: + // If an addin is trying to set selections in an editor that is not the active + // one it is most likely that it was active before the addin ran, but the addin + // opened a something that overtook its' focus. The most likely culprit for + // this is a shiny app. In the case that the target window is visible + // alongside the shiny app, it will be found and used. If it is not visible, + // there's a change it may be the last active, if the shiny app over took it. + // If it is neither of these things a new one needs to be opened to set + // selections and the question is whether open it in the same window as the + // shiny app, or the one 'beside'. 'beside' is preferred since it allows shiny + // apps that work interactively with an open document to behave more smoothly. + // {prefixer} is an example of one of these. + const target = findTargetUri(id); + const targetDocument = await workspace.openTextDocument(target); + const editor = await reuseOrCreateEditor(targetDocument); + + const selectionObjects = ranges.map(x => { + const newRange = parseRange(x, targetDocument); + const newSelection = new Selection(newRange.start, newRange.end); + return (newSelection); + }); + + editor.selections = selectionObjects; +} + +export async function documentSave(id: string): Promise { + const target = findTargetUri(id); + const targetDocument = await workspace.openTextDocument(target); + await targetDocument.save(); +} + +export async function documentSaveAll(): Promise { + await workspace.saveAll(); +} + +// TODO: very similar to ./utils.getCurrentWorkspaceFolder() +export function projectPath(): { path: string | undefined; } { + + if (typeof workspace.workspaceFolders !== 'undefined') { + // Is there a root folder open? + + if (workspace.workspaceFolders.length === 1) { + // In single root common case, this will always work. + return { + path: workspace.workspaceFolders[0].uri.path + }; + } else if (workspace.workspaceFolders.length > 1) { + // In less common multi-root folder case is a bit tricky. If the active + // text editor has scheme 'untitled:' (is unsaved), then + // workspace.getWorkspaceFolder() won't be able to find its Uri in any + // folder and will return undefined. + const currentDocument = getLastActiveTextEditor().document; + const currentDocFolder = workspace.getWorkspaceFolder(currentDocument.uri); + if (typeof currentDocFolder !== 'undefined') { + return { + path: currentDocFolder.uri.path + }; + } + } + } + + // if we got to here either: + // - the workspaceFolders array was undefined (no folder open) + // - the activeText editor was an unsaved document, which has undefined workspace folder. + // return undefined and handle with a message in R. + return { + path: undefined + }; +} + +export async function documentNew(text: string, type: string, position: number[]): Promise { + const currentProjectPath = projectPath().path; + if (!currentProjectPath) { + return; // TODO: Report failure + } + const documentUri = Uri.parse('untitled:' + path.join(currentProjectPath, 'new_document.' + type)); + const targetDocument = await workspace.openTextDocument(documentUri); + const edit = new WorkspaceEdit(); + const docLines = targetDocument.lineCount; + edit.replace(documentUri, + targetDocument.validateRange(new Range( + new Position(0, 0), + new Position(docLines + 1, 0) + )), + text); + + void workspace.applyEdit(edit).then(async () => { + const editor = await window.showTextDocument(targetDocument); + editor.selections = [new Selection( + parsePosition(position, targetDocument), + parsePosition(position, targetDocument) + )]; + }); +} + +// interface +// represents addins in a QuickPick menu +interface AddinItem extends QuickPickItem { + binding: string; + package: string; +} + +let addinQuickPicks: AddinItem[] | undefined = undefined; + +export async function getAddinPickerItems(): Promise { + + if (typeof addinQuickPicks === 'undefined') { + const addins: any[] = await readJSON(path.join(sessionDir, 'addins.json')). + then( + (result) => result, + () => { + throw ('Could not find list of installed addins.' + + ' options(vsc.rstudioapi = TRUE) must be set in your .Rprofile to use ' + + ' RStudio Addins'); + } + ); + + const addinItems = addins.map((x) => { + return { + alwaysShow: false, + description: `{${x.package}}`, + label: x.name, + detail: x.description, + picked: false, + binding: x.binding, + package: x.package, + }; + }); + addinQuickPicks = addinItems; + } + return addinQuickPicks; +} + +export function purgeAddinPickerItems(): void { + addinQuickPicks = undefined; +} + +export async function launchAddinPicker(): Promise { + + if (!config().get('sessionWatcher')) { + void window.showErrorMessage('{rstudioapi} emulation requires session watcher to be enabled in extension config.'); + return; + } + if (!sessionDirectoryExists()) { + void window.showErrorMessage('No active R terminal session, attach one to use RStudio addins.'); + return; + } + + const addinPickerOptions: QuickPickOptions = { + matchOnDescription: true, + matchOnDetail: true, + canPickMany: false, + ignoreFocusOut: false, + placeHolder: '', + onDidSelectItem: undefined + }; + const addinSelection: AddinItem | undefined = + await window.showQuickPick(getAddinPickerItems(), addinPickerOptions); + + if (!(typeof addinSelection === 'undefined')) { + await runTextInTerm(addinSelection.package + ':::' + addinSelection.binding + '()'); + } +} + +export async function sendCodeToRTerminal(code: string, execute: boolean, focus: boolean) { + if (execute) { + console.info(`[sendCodeToRTerminal] sending code: ${code}`); + } else { + console.info(`[sendCodeToRTerminal] inserting code: ${code}`); + } + + await runTextInTerm(code, execute); + if (focus) { + const rTerm = await chooseTerminal(); + if (rTerm !== undefined) { + rTerm.show(); + } + } +} + +//utils +function toVSCCoord(coord: any) { + // this is necessary because RStudio will accept negative or infinite values, + // replacing them with the min or max or the document. + // These must be clamped non-negative integers accepted by VSCode. + // For Inf, we set the value to a very large integer, relying on the + // parsing functions to revise this down using the validatePosition/Range functions. + let coord_value: number; + if (coord === 'Inf') { + coord_value = 10000000; + } else if (coord === '-Inf') { + coord_value = 0; + } else if (coord <= 0) { + coord_value = 0; + } + else { // coord > 0 + coord_value = coord - 1; // positions in the rstudioapi are 1 indexed. + } + + return coord_value; + +} + +function parsePosition(rs_position: any[], targetDocument: TextDocument) { + if (rs_position.length !== 2) { + throw ('an rstudioapi position must be an array of 2 numbers'); + } + return ( + targetDocument.validatePosition( + new Position(toVSCCoord(rs_position[0]), toVSCCoord(rs_position[1])) + )); +} + +function parseRange(rs_range: any, targetDocument: TextDocument) { + if (rs_range.start.length !== 2 || rs_range.end.length !== 2) { + throw ('an rstudioapi range must be an object containing two numeric arrays'); + } + return ( + targetDocument.validateRange( + new Range( + new Position(toVSCCoord(rs_range.start[0]), toVSCCoord(rs_range.start[1])), + new Position(toVSCCoord(rs_range.end[0]), toVSCCoord(rs_range.end[1])) + ) + )); +} + +function assertSupportedEditOperation(operation: string) { + if (operation !== 'insertText' && operation !== 'modifyRange') { + throw ('Operation: ' + operation + ' not supported by VSCode-R API'); + } +} + +function normaliseEditText(text: string, editLocation: any, + operation: string, targetDocument: TextDocument) { + // in a document with lines, does the line position extend past the existing + // lines in the document? rstudioapi adds a newline in this case, so must we. + // n_lines is a count, line is 0 indexed position hence + 1 + const editStartLine = operation === 'insertText' ? + editLocation[0] : + editLocation.start[0]; + if (editStartLine === 'Inf' || + (editStartLine + 1 > targetDocument.lineCount && targetDocument.lineCount > 0)) { + return (text + '\n'); + } else { + return text; + } +} + +// window.onActiveTextEditorDidChange handler +export function trackLastActiveTextEditor(editor?: TextEditor): void { + if (typeof editor !== 'undefined') { + lastActiveTextEditor = editor; + } +} + +function getLastActiveTextEditor() { + return (typeof window.activeTextEditor === 'undefined' ? + lastActiveTextEditor : window.activeTextEditor); +} + +function findTargetUri(id: string | null) { + return (id === null ? + getLastActiveTextEditor().document.uri : Uri.parse(id)); +} + +async function reuseOrCreateEditor(targetDocument: TextDocument) { + // if there's a known text editor for a Uri, use it. if not, open a new one + // 'beside' the current one. We know about the last active, and all visible. + // Sometimes the last active is not visible in the case it was overtaken by a + // WebViewPanel. + + const KnownEditors: TextEditor[] = []; + + KnownEditors.push(lastActiveTextEditor); + KnownEditors.push(...window.visibleTextEditors); + + + const matchingTextEditors = KnownEditors.filter((editor) => + editor.document.uri.toString() === targetDocument.uri.toString()); + + if (matchingTextEditors.length === 0) { + const newEditor = await window.showTextDocument( + targetDocument, + ViewColumn.Beside + ); + return (newEditor); + } + else { + return (matchingTextEditors[0]); + } +} diff --git a/src/selection.ts b/src/selection.ts index b70f7e98e..b3bce7f3d 100644 --- a/src/selection.ts +++ b/src/selection.ts @@ -1,56 +1,87 @@ -"use strict"; - -import { Position, Range, window} from "vscode"; -import { LineCache } from "./lineCache"; - -export function getSelection(): any { - const selection = { linesDownToMoveCursor: 0, selectedTextArray: [] }; - const { start, end } = window.activeTextEditor.selection; - const currentDocument = window.activeTextEditor.document; - const range = new Range(start, end); - - let selectedLine = currentDocument.getText(range); - if (!selectedLine) { - const { startLine, endLine } - = extendSelection(start.line, (x) => currentDocument.lineAt(x).text, currentDocument.lineCount); - const charactersOnLine = window.activeTextEditor.document.lineAt(endLine).text.length; - const newStart = new Position(startLine, 0); - const newEnd = new Position(endLine, charactersOnLine); - selection.linesDownToMoveCursor = 1 + endLine - start.line; - selectedLine = currentDocument.getText(new Range(newStart, newEnd)); - } else if (start.line === end.line) { - selection.linesDownToMoveCursor = 0; - selection.selectedTextArray = [currentDocument.getText(new Range(start, end))]; - return selection; +'use strict'; + +import { Position, Range, window } from 'vscode'; + +import { LineCache } from './lineCache'; +import { config } from './util'; + +export function getWordOrSelection(): string | undefined { + const textEditor = window.activeTextEditor; + if (!textEditor) { + return; + } + const selection = textEditor.selection; + const currentDocument = textEditor.document; + let text: string; + if ((selection.start.line === selection.end.line) && + (selection.start.character === selection.end.character)) { + const wordRange = currentDocument.getWordRangeAtPosition(selection.start); + text = currentDocument.getText(wordRange); } else { - selectedLine = currentDocument.getText(new Range(start, end)); + text = currentDocument.getText(textEditor.selection); } - const selectedTextArray = selectedLine.split("\n"); - selection.selectedTextArray = removeCommentedLines(selectedTextArray); + return text; +} - return selection; +export function surroundSelection(text: string, rFunctionName: string[]): string { + if (rFunctionName && rFunctionName.length) { + let rFunctionCall = ''; + for (const feature of rFunctionName) { + rFunctionCall += `${feature}(`; + } + text = rFunctionCall + text.trim() + ')'.repeat(rFunctionName.length); + } + + return text; } -function removeCommentedLines(selection: string[]): string[] { - const selectionWithoutComments = []; - selection.forEach((line) => { - if (!checkForBlankOrComment(line)) { selectionWithoutComments.push(line); } - }); - return selectionWithoutComments; +export interface RSelection { + linesDownToMoveCursor: number; + selectedText: string; + startLine: number; + endLine: number; + range: Range; } -export function checkForBlankOrComment(line: string): boolean { - let index = 0; - let isWhitespaceOnly = true; - while (index < line.length) { - if (!((line[index] === " ") || (line[index] === "\t") || (line[index] === "\r"))) { - isWhitespaceOnly = false; - break; - } - index++; +export function getSelection(): RSelection | undefined { + const textEditor = window.activeTextEditor; + if (!textEditor) { + return; + } + + const currentDocument = textEditor.document; + const { start, end } = textEditor.selection; + const selection = { + linesDownToMoveCursor: 0, + selectedText: '', + startLine: start.line, + endLine: end.line, + range: new Range(start, end) + }; + + if (selection.range.isEmpty) { + const {startLine, endLine} = extendSelection( + start.line, + (x) => currentDocument.lineAt(x).text, + currentDocument.lineCount + ); + const charactersOnLine = textEditor.document.lineAt(endLine).text.length; + const newStart = new Position(startLine, 0); + const newEnd = new Position(endLine, charactersOnLine); + selection.linesDownToMoveCursor = endLine + 1 - start.line; + selection.range = new Range(newStart, newEnd); } - return isWhitespaceOnly || line[index] === "#"; + + let selectedText = currentDocument.getText(selection.range).trim(); + + if (config().get('removeLeadingComments')) { + selectedText = removeLeadingComments(selectedText); + } + + selection.selectedText = selectedText; + + return selection; } /** @@ -58,25 +89,28 @@ export function checkForBlankOrComment(line: string): boolean { */ class PositionNeg { public line: number; - public character; - public cter: number; - constructor(line: number, character: number) { + public character: number; + public constructor(line: number, character: number) { this.line = line; this.character = character; } } function doBracketsMatch(a: string, b: string): boolean { - const matches = { "(": ")", "[": "]", "{": "}", ")": "(", "]": "[", "}": "{" }; - return matches[a] === b; + const matches = new Map(Object.entries({ '(': ')', '[': ']', '{': '}', ')': '(', ']': '[', '}': '{' })); + return matches.get(a) === b; } function isBracket(c: string, lookingForward: boolean) { if (lookingForward) { - return ((c === "(") || (c === "[") || (c === "{")); - } else { - return ((c === ")") || (c === "]") || (c === "}")); + return ((c === '(') || (c === '[') || (c === '{')); } + + return ((c === ')') || (c === ']') || (c === '}')); +} + +function isQuote(c: string) { + return c === '"' || c === '\'' || c === '`'; } /** @@ -89,13 +123,15 @@ function isBracket(c: string, lookingForward: boolean) { * @param getEndsInOperator A function that returns whether the given line ends in an operator. * @param lineCount The number of lines in the document. */ -function getNextChar(p: PositionNeg, - lookingForward: boolean, - getLine: (x: number) => string, - getEndsInOperator: (y: number) => boolean, - lineCount) { +function getNextChar( + p: PositionNeg, + lookingForward: boolean, + getLine: (x: number) => string, + getEndsInOperator: (y: number) => boolean, + lineCount: number +){ const s = getLine(p.line); - let nextPos: PositionNeg = null; + let nextPos: PositionNeg; let isEndOfCodeLine = false; let isEndOfFile = false; if (lookingForward) { @@ -131,6 +167,7 @@ function getNextChar(p: PositionNeg, } } const nextChar = getLine(nextPos.line)[nextPos.character]; + return ({ nextChar, nextPos, isEndOfCodeLine, isEndOfFile }); } @@ -169,39 +206,67 @@ function getNextChar(p: PositionNeg, * @param getLine A function that returns the string at the given line of the document. * @param lineCount The number of lines in the document. */ -export function extendSelection(line: number, getLine: (line: number) => string, lineCount: number) { +export function extendSelection(line: number, getLine: (line: number) => string, lineCount: number): { + startLine: number; + endLine: number; +} { const lc = new LineCache(getLine, lineCount); - const getLineFromCache = (x) => lc.getLineFromCache(x); - const getEndsInOperatorFromCache = (x) => lc.getEndsInOperatorFromCache(x); + const getLineFromCache = (x: number) => lc.getLineFromCache(x); + const getEndsInOperatorFromCache = (x: number) => lc.getEndsInOperatorFromCache(x); let lookingForward = true; - // poss[1] is the farthest point reached looking forward from line, - // and poss[0] is the farthest point reached looking backward from line. + /* poss[1] is the farthest point reached looking forward from line, + and poss[0] is the farthest point reached looking backward from line. */ const poss = { 0: new PositionNeg(line, 0), 1: new PositionNeg(line, -1) }; const flagsFinish = { 0: false, 1: false }; // 1 represents looking forward, 0 represents looking back. let flagAbort = false; - const unmatched = { 0: [] as string[], 1: [] as string[]}; + const unmatched = { 0: [] as string[], 1: [] as string[] }; + let curChar = ''; + let quoteChar = ''; while (!flagAbort && !(flagsFinish[0] && flagsFinish[1])) { - const { nextChar, nextPos, isEndOfCodeLine, isEndOfFile } - = getNextChar(poss[lookingForward ? 1 : 0], - lookingForward, - getLineFromCache, - getEndsInOperatorFromCache, - lineCount); + const { nextChar, nextPos, isEndOfCodeLine, isEndOfFile } = getNextChar( + poss[lookingForward ? 1 : 0], + lookingForward, + getLineFromCache, + getEndsInOperatorFromCache, + lineCount + ); poss[lookingForward ? 1 : 0] = nextPos; - if (isBracket(nextChar, lookingForward)) { - unmatched[lookingForward ? 1 : 0].push(nextChar); - } else if (isBracket(nextChar, !lookingForward)) { - if (unmatched[lookingForward ? 1 : 0].length === 0) { - lookingForward = !lookingForward; - unmatched[lookingForward ? 1 : 0].push(nextChar); - flagsFinish[lookingForward ? 1 : 0] = false; + if (quoteChar === '') { + if (isQuote(nextChar)) { + quoteChar = nextChar; } else { - const needsToMatch = unmatched[lookingForward ? 1 : 0].pop(); - if (!doBracketsMatch(nextChar, needsToMatch)) { - flagAbort = true; + if (isBracket(nextChar, lookingForward)) { + unmatched[lookingForward ? 1 : 0].push(nextChar); + } else if (isBracket(nextChar, !lookingForward)) { + if (unmatched[lookingForward ? 1 : 0].length === 0) { + lookingForward = !lookingForward; + unmatched[lookingForward ? 1 : 0].push(nextChar); + flagsFinish[lookingForward ? 1 : 0] = false; + } else if (!doBracketsMatch(nextChar, unmatched[lookingForward ? 1 : 0].pop() ?? '')) { + flagAbort = true; + } + } + } + } else { + if (nextChar === quoteChar) { + if (lookingForward) { + if (curChar !== '\\') { + quoteChar = ''; + } + } else { + const next = getNextChar(poss[lookingForward ? 1 : 0], + lookingForward, + getLineFromCache, + getEndsInOperatorFromCache, + lineCount); + if (next.nextChar !== '\\') { + quoteChar = ''; + } } } - } else if (isEndOfCodeLine) { + } + + if (isEndOfCodeLine) { if (unmatched[lookingForward ? 1 : 0].length === 0) { // We have found everything we need to in this direction. Continue looking in the other direction. flagsFinish[lookingForward ? 1 : 0] = true; @@ -211,10 +276,32 @@ export function extendSelection(line: number, getLine: (line: number) => string, flagAbort = true; } } + + curChar = nextChar; } if (flagAbort) { return ({ startLine: line, endLine: line }); - } else { - return ({ startLine: poss[0].line, endLine: poss[1].line }); } + + return ({ startLine: poss[0].line, endLine: poss[1].line }); +} + + +/** + * This function removes leading R comments from a block of code text + * I.e. All blank and commented out lines are removed up until we hit the first + * non-blank / non-comment line. + * @param text A block of R code as a string + */ +export function removeLeadingComments(text: string): string { + const textArray = text.split('\n'); + let endSearchIndex = 0; + for (const lineContent of textArray) { + if (lineContent.search('(^ *$|^ *#)') !== -1) { + endSearchIndex += 1; + } else { + break; + } + } + return textArray.slice(endSearchIndex).join('\n'); } diff --git a/src/session.ts b/src/session.ts new file mode 100644 index 000000000..b5959019a --- /dev/null +++ b/src/session.ts @@ -0,0 +1,914 @@ +'use strict'; + +import * as fs from 'fs-extra'; +import * as os from 'os'; +import * as path from 'path'; +import { Agent } from 'http'; +import fetch from 'node-fetch'; +import { commands, StatusBarItem, Uri, ViewColumn, Webview, window, workspace, env, WebviewPanelOnDidChangeViewStateEvent, WebviewPanel } from 'vscode'; + +import { runTextInTerm } from './rTerminal'; +import { FSWatcher } from 'fs-extra'; +import { config, readContent, setContext, UriIcon } from './util'; +import { purgeAddinPickerItems, dispatchRStudioAPICall } from './rstudioapi'; + +import { IRequest } from './liveShare/shareSession'; +import { homeExtDir, rWorkspace, globalRHelp, globalHttpgdManager, extensionContext, sessionStatusBarItem } from './extension'; +import { UUID, rHostService, rGuestService, isLiveShare, isHost, isGuestSession, closeBrowser, guestResDir, shareBrowser, openVirtualDoc, shareWorkspace } from './liveShare'; + +export interface GlobalEnv { + [key: string]: { + class: string[]; + type: string; + length: number; + str: string; + size?: number; + dim?: number[], + names?: string[], + slots?: string[] + } +} + +export interface WorkspaceData { + search: string[]; + loaded_namespaces: string[]; + globalenv: GlobalEnv; +} + +export interface SessionServer { + host: string; + port: number; + token: string; +} + +export let workspaceData: WorkspaceData; +let resDir: string; +export let requestFile: string; +export let requestLockFile: string; +let requestTimeStamp: number; +let responseTimeStamp: number; +export let sessionDir: string; +export let workingDir: string; +let rVer: string; +let pid: string; +// eslint-disable-next-line @typescript-eslint/no-explicit-any +let info: any; +const httpAgent = new Agent({ keepAlive: true }); +export let server: SessionServer | undefined; +export let workspaceFile: string; +let workspaceLockFile: string; +let workspaceTimeStamp: number; +let plotFile: string; +let plotLockFile: string; +let plotTimeStamp: number; +let workspaceWatcher: FSWatcher; +let plotWatcher: FSWatcher; +let activeBrowserPanel: WebviewPanel | undefined; +let activeBrowserUri: Uri | undefined; +let activeBrowserExternalUri: Uri | undefined; + +export function deploySessionWatcher(extensionPath: string): void { + console.info(`[deploySessionWatcher] extensionPath: ${extensionPath}`); + resDir = path.join(extensionPath, 'dist', 'resources'); + + const initPath = path.join(extensionPath, 'R', 'session', 'init.R'); + const linkPath = path.join(homeExtDir(), 'init.R'); + fs.writeFileSync(linkPath, `local(source("${initPath.replace(/\\/g, '\\\\')}", chdir = TRUE, local = TRUE))\n`); + + writeSettings(); + workspace.onDidChangeConfiguration(event => { + if (event.affectsConfiguration('r')) { + writeSettings(); + } + }); +} + +export function startRequestWatcher(sessionStatusBarItem: StatusBarItem): void { + console.info('[startRequestWatcher] Starting'); + requestFile = path.join(homeExtDir(), 'request.log'); + requestLockFile = path.join(homeExtDir(), 'request.lock'); + requestTimeStamp = 0; + responseTimeStamp = 0; + if (!fs.existsSync(requestLockFile)) { + fs.createFileSync(requestLockFile); + } + fs.watch(requestLockFile, {}, () => { + void updateRequest(sessionStatusBarItem); + }); + console.info('[startRequestWatcher] Done'); +} + +export function attachActive(): void { + if (config().get('sessionWatcher')) { + console.info('[attachActive]'); + void runTextInTerm('.vsc.attach()'); + if (isLiveShare() && shareWorkspace) { + rHostService?.notifyRequest(requestFile, true); + } + } else { + void window.showInformationMessage('This command requires that r.sessionWatcher be enabled.'); + } +} + +export function removeDirectory(dir: string): void { + console.info(`[removeDirectory] dir: ${dir}`); + if (fs.existsSync(dir)) { + console.info('[removeDirectory] dir exists'); + fs.readdirSync(dir) + .forEach((file) => { + const curPath = path.join(dir, file); + console.info(`[removeDirectory] Remove ${curPath}`); + fs.unlinkSync(curPath); + }); + console.info(`[removeDirectory] Remove dir ${dir}`); + fs.rmdirSync(dir); + } + console.info('[removeDirectory] Done'); +} + +export function sessionDirectoryExists(): boolean { + return (fs.existsSync(sessionDir)); +} + +export function removeSessionFiles(): void { + console.info('[removeSessionFiles] ', sessionDir); + if (sessionDirectoryExists()) { + removeDirectory(sessionDir); + } + console.info('[removeSessionFiles] Done'); +} + +function writeSettings() { + const settingPath = path.join(homeExtDir(), 'settings.json'); + fs.writeFileSync(settingPath, JSON.stringify(config())); +} + +function updateSessionWatcher() { + console.info(`[updateSessionWatcher] PID: ${pid}`); + console.info('[updateSessionWatcher] Create workspaceWatcher'); + workspaceFile = path.join(sessionDir, 'workspace.json'); + workspaceLockFile = path.join(sessionDir, 'workspace.lock'); + workspaceTimeStamp = 0; + if (workspaceWatcher !== undefined) { + workspaceWatcher.close(); + } + if (fs.existsSync(workspaceLockFile)) { + workspaceWatcher = fs.watch(workspaceLockFile, {}, () => { + void updateWorkspace(); + }); + void updateWorkspace(); + } else { + console.info('[updateSessionWatcher] workspaceLockFile not found'); + } + + console.info('[updateSessionWatcher] Create plotWatcher'); + plotFile = path.join(sessionDir, 'plot.png'); + plotLockFile = path.join(sessionDir, 'plot.lock'); + plotTimeStamp = 0; + if (plotWatcher !== undefined) { + plotWatcher.close(); + } + if (fs.existsSync(plotLockFile)) { + plotWatcher = fs.watch(plotLockFile, {}, () => { + void updatePlot(); + }); + void updatePlot(); + } else { + console.info('[updateSessionWatcher] plotLockFile not found'); + } + console.info('[updateSessionWatcher] Done'); +} + +async function updatePlot() { + console.info(`[updatePlot] ${plotFile}`); + const lockContent = await fs.readFile(plotLockFile, 'utf8'); + const newTimeStamp = Number.parseFloat(lockContent); + if (newTimeStamp !== plotTimeStamp) { + plotTimeStamp = newTimeStamp; + if (fs.existsSync(plotFile) && fs.statSync(plotFile).size > 0) { + void commands.executeCommand('vscode.open', Uri.file(plotFile), { + preserveFocus: true, + preview: true, + viewColumn: ViewColumn[(config().get('session.viewers.viewColumn.plot') || 'Two') as keyof typeof ViewColumn], + }); + console.info('[updatePlot] Done'); + if (isLiveShare()) { + void rHostService?.notifyPlot(plotFile); + } + } else { + console.info('[updatePlot] File not found'); + } + } +} + +async function updateWorkspace() { + console.info(`[updateWorkspace] ${workspaceFile}`); + + const lockContent = await fs.readFile(workspaceLockFile, 'utf8'); + const newTimeStamp = Number.parseFloat(lockContent); + if (newTimeStamp !== workspaceTimeStamp) { + workspaceTimeStamp = newTimeStamp; + if (fs.existsSync(workspaceFile)) { + const content = await fs.readFile(workspaceFile, 'utf8'); + workspaceData = JSON.parse(content) as WorkspaceData; + void rWorkspace?.refresh(); + console.info('[updateWorkspace] Done'); + if (isLiveShare()) { + rHostService?.notifyWorkspace(workspaceData); + } + } else { + console.info('[updateWorkspace] File not found'); + } + } +} + +export async function showBrowser(url: string, title: string, viewer: string | boolean): Promise { + console.info(`[showBrowser] uri: ${url}, viewer: ${viewer.toString()}`); + const uri = Uri.parse(url); + if (viewer === false) { + void env.openExternal(uri); + } else { + const externalUri = await env.asExternalUri(uri); + const panel = window.createWebviewPanel( + 'browser', + title, + { + preserveFocus: true, + viewColumn: ViewColumn[String(viewer) as keyof typeof ViewColumn], + }, + { + enableFindWidget: true, + enableScripts: true, + retainContextWhenHidden: true, + }); + if (isHost()) { + await shareBrowser(url, title); + } + panel.onDidChangeViewState((e: WebviewPanelOnDidChangeViewStateEvent) => { + if (e.webviewPanel.active) { + activeBrowserPanel = panel; + activeBrowserUri = uri; + activeBrowserExternalUri = externalUri; + } else { + activeBrowserPanel = undefined; + activeBrowserUri = undefined; + activeBrowserExternalUri = undefined; + } + void commands.executeCommand('setContext', 'r.browser.active', e.webviewPanel.active); + }); + panel.onDidDispose(() => { + activeBrowserPanel = undefined; + activeBrowserUri = undefined; + activeBrowserExternalUri = undefined; + if (isHost()) { + closeBrowser(url); + } + void commands.executeCommand('setContext', 'r.browser.active', false); + }); + panel.iconPath = new UriIcon('globe'); + panel.webview.html = getBrowserHtml(externalUri); + } + console.info('[showBrowser] Done'); +} + +function getBrowserHtml(uri: Uri): string { + return ` + + + + + + + + +